mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 18:17:43 +00:00
Merge branch 'develop' of https://github.com/sleuthkit/autopsy into 7328-CheckboxListDiscoveryFilters
This commit is contained in:
commit
6d74fd79b1
@ -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");
|
||||
@ -87,6 +87,7 @@ 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.OsAccountRemovedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.PersonsAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.PersonsChangedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.PersonsRemovedEvent;
|
||||
@ -124,6 +125,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;
|
||||
@ -143,8 +145,8 @@ 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.OsAccountsDeleteEvent;
|
||||
import org.sleuthkit.datamodel.OsAccountManager.OsAccountsUpdateEvent;
|
||||
import org.sleuthkit.datamodel.Person;
|
||||
import org.sleuthkit.datamodel.PersonManager.PersonsCreationEvent;
|
||||
@ -442,6 +444,10 @@ public class Case {
|
||||
* 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.
|
||||
@ -520,6 +526,13 @@ public class Case {
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void publishOsAccountDeletedEvent(OsAccountsDeleteEvent event) {
|
||||
for(OsAccount account: event.getOsAcounts()) {
|
||||
eventPublisher.publish(new OsAccountRemovedEvent(account));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes an autopsy event from the sleuthkit HostCreationEvent
|
||||
* indicating that hosts have been created.
|
||||
@ -1478,16 +1491,7 @@ 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();
|
||||
}
|
||||
|
||||
return path.toAbsolutePath().toString();
|
||||
return UserMachinePreferences.getTempDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1798,6 +1802,10 @@ public class Case {
|
||||
eventPublisher.publish(new OsAccountChangedEvent(account));
|
||||
}
|
||||
|
||||
public void notifyOsAccountRemoved(OsAccount account) {
|
||||
eventPublisher.publish(new OsAccountRemovedEvent(account));
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify via an autopsy event that a host has been added.
|
||||
* @param host The host that has been added.
|
||||
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.datamodel.OsAccount;
|
||||
|
||||
/**
|
||||
* Event published when an OsAccount is deleted.
|
||||
*/
|
||||
public final class OsAccountRemovedEvent extends OsAccountEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public OsAccountRemovedEvent(OsAccount account) {
|
||||
super(Case.Events.OS_ACCOUNT_REMOVED.toString(), account);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -192,7 +192,7 @@ 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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
@ -123,6 +123,15 @@ class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider {
|
||||
return proxyLookup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the explorerManager for the table.
|
||||
*
|
||||
* @return The explorer manager for the table.
|
||||
*/
|
||||
ExplorerManager getExplorerManager() {
|
||||
return outlineViewPanel.getExplorerManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
|
@ -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();
|
||||
@ -403,6 +405,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 {
|
||||
super.validate();
|
||||
|
@ -674,8 +674,7 @@ public final class UserPreferences {
|
||||
* @return The absolute path to the application temp directory.
|
||||
*/
|
||||
public static String getAppTempDirectory() {
|
||||
return Paths.get(UserMachinePreferences.getBaseTempDirectory(), getAppName())
|
||||
.toAbsolutePath().toString();
|
||||
return UserMachinePreferences.getTempDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
@ -418,7 +420,7 @@
|
||||
<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 +446,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"/>
|
||||
<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"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="tempOnCustomNoPath" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="tempDirectoryField" min="-2" pref="367" max="-2" 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>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace pref="158" 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 +516,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");
|
||||
@ -38,6 +38,7 @@ import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.netbeans.spi.options.OptionsPanelController;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
@ -47,9 +48,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;
|
||||
|
||||
/**
|
||||
@ -77,7 +78,6 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
private static final long serialVersionUID = 1L;
|
||||
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
|
||||
@ -87,6 +87,8 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
private static final Logger logger = Logger.getLogger(AutopsyOptionsPanel.class.getName());
|
||||
private String initialMemValue = Long.toString(Runtime.getRuntime().maxMemory() / ONE_BILLION);
|
||||
|
||||
private final ReportBranding reportBranding;
|
||||
|
||||
/**
|
||||
* Instantiate the Autopsy options panel.
|
||||
*/
|
||||
@ -116,10 +118,12 @@ 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();
|
||||
TextFieldListener textFieldListener = new TextFieldListener();
|
||||
agencyLogoPathField.getDocument().addDocumentListener(textFieldListener);
|
||||
tempDirectoryField.getDocument().addDocumentListener(textFieldListener);
|
||||
tempCustomField.getDocument().addDocumentListener(new TempCustomTextListener());
|
||||
logFileCount.setText(String.valueOf(UserPreferences.getLogFileCount()));
|
||||
|
||||
reportBranding = new ReportBranding();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -299,20 +303,45 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
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) {
|
||||
@ -333,9 +362,13 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
|
||||
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 +400,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 +418,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,10 +453,10 @@ 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
|
||||
@ -551,6 +609,32 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for changes in the temp directory custom directory text field.
|
||||
*/
|
||||
private class TempCustomTextListener extends TextFieldListener {
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
evaluateTempDirState();
|
||||
super.changedUpdate(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
evaluateTempDirState();
|
||||
super.changedUpdate(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
evaluateTempDirState();
|
||||
super.changedUpdate(e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
@ -563,6 +647,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();
|
||||
@ -589,9 +674,13 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
solrMaxHeapSpinner = new javax.swing.JSpinner();
|
||||
solrJVMHeapWarning = 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();
|
||||
@ -818,7 +907,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 +919,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 +953,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(158, 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 +1036,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 +1057,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 +1101,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 +1147,21 @@ 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
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JTextField agencyLogoPathField;
|
||||
private javax.swing.JLabel agencyLogoPathFieldValidationLabel;
|
||||
@ -1045,10 +1190,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
|
||||
|
||||
|
@ -188,12 +188,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 +246,8 @@ 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.
|
||||
|
@ -12,6 +12,8 @@ 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_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
|
||||
@ -246,12 +248,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 +306,8 @@ 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.
|
||||
|
@ -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;
|
||||
@ -287,10 +289,13 @@ 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);
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
@ -300,17 +305,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()]);
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +114,8 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
if(eventType.equals(Case.Events.OS_ACCOUNT_ADDED.toString())) {
|
||||
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
|
||||
@ -128,7 +129,7 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,21 @@ AddEditHostDialog.okButton.text=OK
|
||||
AddEditHostDialog.cancelButton.text=Cancel
|
||||
AddEditHostDialog.inputTextField.text=jTextField1
|
||||
ManageHostsDialog_title_text=Manage Hosts
|
||||
# {0} - sourceHost
|
||||
# {1} - destHost
|
||||
MergeHostAction.confirmText=Are you sure you want to merge {0} into {1}?\nThis may include merging OS Accounts and cannot be undone.
|
||||
MergeHostAction.confirmTitle=Confirmation
|
||||
MergeHostAction.errorText=An error occurred while merging hosts.\nTry again in a few minutes or check the log for details.
|
||||
MergeHostAction.errorTitle=Error Merging Hosts
|
||||
MergeHostAction.progressIndicatorName=Merging Hosts
|
||||
# {0} - sourceHost
|
||||
# {1} - destHost
|
||||
MergeHostAction.progressText=Merging {0} into {1}...
|
||||
# {0} - sourceHostName
|
||||
# {1} - destHostName
|
||||
MergeHostAction_onError_description=There was an error merging host {0} into host {1}.
|
||||
MergeHostAction_onError_title=Error Merging Hosts
|
||||
MergeHostMenuAction_menuTitle=Merge Into Other Host
|
||||
OpenHostsAction_displayName=Hosts
|
||||
# {0} - personName
|
||||
RemoveParentPersonAction_menuTitle=Remove from Person ({0})
|
||||
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.hosts;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator;
|
||||
import org.sleuthkit.autopsy.progress.ProgressIndicator;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
|
||||
/**
|
||||
* Menu action to merge a host into another host.
|
||||
*/
|
||||
@Messages({
|
||||
"MergeHostAction_onError_title=Error Merging Hosts",
|
||||
"# {0} - sourceHostName",
|
||||
"# {1} - destHostName",
|
||||
"MergeHostAction_onError_description=There was an error merging host {0} into host {1}.",})
|
||||
public class MergeHostAction extends AbstractAction {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(MergeHostAction.class.getName());
|
||||
|
||||
private final Host sourceHost;
|
||||
private final Host destHost;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param sourceHost The source host.
|
||||
* @param destHost The destination host.
|
||||
*/
|
||||
public MergeHostAction(Host sourceHost, Host destHost) {
|
||||
super(destHost.getName());
|
||||
|
||||
this.sourceHost = sourceHost;
|
||||
this.destHost = destHost;
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"MergeHostAction.progressIndicatorName=Merging Hosts",
|
||||
"MergeHostAction.confirmTitle=Confirmation",
|
||||
"# {0} - sourceHost",
|
||||
"# {1} - destHost",
|
||||
"MergeHostAction.confirmText=Are you sure you want to merge {0} into {1}?\nThis may include merging OS Accounts and cannot be undone.",
|
||||
"# {0} - sourceHost",
|
||||
"# {1} - destHost",
|
||||
"MergeHostAction.progressText=Merging {0} into {1}..."
|
||||
})
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
|
||||
// Display confirmation dialog
|
||||
int response = JOptionPane.showConfirmDialog(
|
||||
WindowManager.getDefault().getMainWindow(),
|
||||
NbBundle.getMessage(this.getClass(), "MergeHostAction.confirmText", sourceHost.getName(), destHost.getName()),
|
||||
NbBundle.getMessage(this.getClass(), "MergeHostAction.confirmTitle"),
|
||||
JOptionPane.YES_NO_OPTION);
|
||||
if (response == JOptionPane.NO_OPTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModalDialogProgressIndicator progressDialog = new ModalDialogProgressIndicator(WindowManager.getDefault().getMainWindow(),
|
||||
Bundle.MergeHostAction_progressIndicatorName());
|
||||
|
||||
MergeHostsBackgroundTask mergeTask = new MergeHostsBackgroundTask(sourceHost, destHost, progressDialog);
|
||||
progressDialog.start(NbBundle.getMessage(this.getClass(), "MergeHostAction.progressText", sourceHost.getName(), destHost.getName()));
|
||||
mergeTask.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the host in a background worker.
|
||||
*/
|
||||
private class MergeHostsBackgroundTask extends SwingWorker<Void, Void> {
|
||||
|
||||
private final Host sourceHost;
|
||||
private final Host destHost;
|
||||
private final ProgressIndicator progress;
|
||||
|
||||
public MergeHostsBackgroundTask(Host sourceHost, Host destHost, ProgressIndicator progress) {
|
||||
this.sourceHost = sourceHost;
|
||||
this.destHost = destHost;
|
||||
this.progress = progress;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().mergeHosts(sourceHost, destHost);
|
||||
return null;
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"MergeHostAction.errorTitle=Error Merging Hosts",
|
||||
"MergeHostAction.errorText=An error occurred while merging hosts.\nTry again in a few minutes or check the log for details."
|
||||
})
|
||||
@Override
|
||||
protected void done() {
|
||||
progress.finish();
|
||||
try {
|
||||
get();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error merging " + sourceHost.getName() + " into " + destHost.getName(), ex);
|
||||
|
||||
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
|
||||
NbBundle.getMessage(this.getClass(), "MergeHostAction.errorText"),
|
||||
NbBundle.getMessage(this.getClass(), "MergeHostAction.errorTitle"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.hosts;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.actions.Presenter;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
*
|
||||
* JMenu item to show a menu allowing the selected host to be merged into another host.
|
||||
*/
|
||||
@Messages({
|
||||
"MergeHostMenuAction_menuTitle=Merge Into Other Host",})
|
||||
public class MergeHostMenuAction extends AbstractAction implements Presenter.Popup {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(MergeHostMenuAction.class.getName());
|
||||
|
||||
private final Host sourceHost;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param host The original host.
|
||||
*/
|
||||
public MergeHostMenuAction(Host host) {
|
||||
super("");
|
||||
this.sourceHost = host;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("NoopMethodInAbstractClass")
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public JMenuItem getPopupPresenter() {
|
||||
JMenu menu = new JMenu(Bundle.MergeHostMenuAction_menuTitle());
|
||||
|
||||
// Get a list of all other hosts
|
||||
List<Host> otherHosts = Collections.emptyList();
|
||||
try {
|
||||
otherHosts = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getHosts();
|
||||
otherHosts.remove(sourceHost);
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error getting hosts for case.", ex);
|
||||
}
|
||||
|
||||
// If there are no other hosts, disable the menu item. Otherwise add
|
||||
// the other hosts to the menu.
|
||||
if (otherHosts.isEmpty()) {
|
||||
menu.setEnabled(false);
|
||||
} else {
|
||||
menu.setEnabled(true);
|
||||
otherHosts.stream()
|
||||
.filter(p -> p != null && p.getName() != null)
|
||||
.sorted((a, b) -> a.getName().compareToIgnoreCase(b.getName()))
|
||||
.map(p -> new JMenuItem(new MergeHostAction(sourceHost, p)))
|
||||
.forEach(menu::add);
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.EventUpdateHandler;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport.ExcelCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.DefaultMenuItem;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
|
||||
|
@ -6,7 +6,21 @@ AnalysisPanel_keywordHits_tabName=Keyword Hits
|
||||
AnalysisPanel_keywordSearchModuleName=Keyword Search
|
||||
BaseDataSourceSummaryPanel_goToArtifact=View Source Result
|
||||
BaseDataSourceSummaryPanel_goToFile=View Source File in Directory
|
||||
ContainerPanel_export_acquisitionDetails=Acquisition Details:
|
||||
ContainerPanel_export_deviceId=Device ID:
|
||||
ContainerPanel_export_displayName=Display Name:
|
||||
ContainerPanel_export_filePaths=File Paths:
|
||||
ContainerPanel_export_imageType=Image Type:
|
||||
ContainerPanel_export_md5=MD5:
|
||||
ContainerPanel_export_originalName=Name:
|
||||
ContainerPanel_export_sectorSize=Sector Size:
|
||||
ContainerPanel_export_sha1=SHA1:
|
||||
ContainerPanel_export_sha256=SHA256:
|
||||
ContainerPanel_export_size=Size:
|
||||
ContainerPanel_export_timeZone=Time Zone:
|
||||
ContainerPanel_export_unallocatedSize=Unallocated Space:
|
||||
ContainerPanel_setFieldsForNonImageDataSource_na=N/A
|
||||
ContainerPanel_tabName=Container
|
||||
CTL_DataSourceSummaryAction=Data Source Summary
|
||||
DataSourceSummaryDialog.closeButton.text=Close
|
||||
ContainerPanel.displayNameLabel.text=Display Name:
|
||||
@ -73,6 +87,12 @@ GeolocationPanel_mostCommon_tabName=Most Common Cities
|
||||
GeolocationPanel_mostRecent_tabName=Most Recent Cities
|
||||
GeolocationPanel_onNoCrIngest_message=No results will be shown because the GPX Parser was not run.
|
||||
GeolocationPanel_unknownRow_title=Unknown
|
||||
IngestJobExcelExport_endTimeColumn=End Time
|
||||
IngestJobExcelExport_ingestStatusTimeColumn=Ingest Status
|
||||
IngestJobExcelExport_moduleNameTimeColumn=Module Name
|
||||
IngestJobExcelExport_sheetName=Ingest History
|
||||
IngestJobExcelExport_startTimeColumn=Start Time
|
||||
IngestJobExcelExport_versionColumn=Module Version
|
||||
PastCasesPanel_caseColumn_title=Case
|
||||
PastCasesPanel_countColumn_title=Count
|
||||
PastCasesPanel_notableFileTable_tabName=Cases with Common Notable
|
||||
@ -87,18 +107,19 @@ RecentFilesPanel_attachmentsTable_tabName=Recent Attachments
|
||||
RecentFilesPanel_col_head_date=Date
|
||||
RecentFilesPanel_docsTable_tabName=Recently Opened Documents
|
||||
RecentFilesPanel_downloadsTable_tabName=Recently Downloads
|
||||
SizeRepresentationUtil_units_bytes=\ bytes
|
||||
SizeRepresentationUtil_units_gigabytes=\ GB
|
||||
SizeRepresentationUtil_units_kilobytes=\ kB
|
||||
SizeRepresentationUtil_units_megabytes=\ MB
|
||||
SizeRepresentationUtil_units_petabytes=\ PB
|
||||
SizeRepresentationUtil_units_terabytes=\ TB
|
||||
SizeRepresentationUtil_units_bytes=bytes
|
||||
SizeRepresentationUtil_units_gigabytes=GB
|
||||
SizeRepresentationUtil_units_kilobytes=KB
|
||||
SizeRepresentationUtil_units_megabytes=MB
|
||||
SizeRepresentationUtil_units_petabytes=PB
|
||||
SizeRepresentationUtil_units_terabytes=TB
|
||||
TimelinePanel_earliestLabel_title=Earliest
|
||||
TimelinePanel_latestLabel_title=Latest
|
||||
TimlinePanel_last30DaysChart_artifactEvts_title=Result Events
|
||||
TimlinePanel_last30DaysChart_fileEvts_title=File Events
|
||||
TimlinePanel_last30DaysChart_title=Last 30 Days
|
||||
TypesPanel_artifactsTypesPieChart_title=Artifact Types
|
||||
TypesPanel_excelTabName=Types
|
||||
TypesPanel_fileMimeTypesChart_audio_title=Audio
|
||||
TypesPanel_fileMimeTypesChart_documents_title=Documents
|
||||
TypesPanel_fileMimeTypesChart_executables_title=Executables
|
||||
|
@ -19,21 +19,35 @@
|
||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
|
||||
import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getFetchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ExcelItemExportable;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.KeyValueItemExportable;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.SingleCellExportable;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.TitledExportable;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.UpdateGovernor;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
@ -42,39 +56,184 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
/**
|
||||
* Panel to display additional details associated with a specific DataSource
|
||||
*/
|
||||
@Messages({
|
||||
"ContainerPanel_tabName=Container"
|
||||
})
|
||||
class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
/**
|
||||
* Data payload for the Container panel.
|
||||
* View model data for data source images.
|
||||
*/
|
||||
private static class ContainerPanelData {
|
||||
private static class ImageViewModel {
|
||||
|
||||
private final DataSource dataSource;
|
||||
private final Long unallocatedFilesSize;
|
||||
private final long unallocatedSize;
|
||||
private final long size;
|
||||
private final long sectorSize;
|
||||
|
||||
private final String timeZone;
|
||||
private final String imageType;
|
||||
|
||||
private final List<String> paths;
|
||||
private final String md5Hash;
|
||||
private final String sha1Hash;
|
||||
private final String sha256Hash;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param dataSource The original datasource.
|
||||
* @param unallocatedFilesSize The unallocated file size.
|
||||
* @param unallocatedSize Size in bytes of unallocated space.
|
||||
* @param size Total size in bytes.
|
||||
* @param sectorSize Sector size in bytes.
|
||||
* @param timeZone The time zone.
|
||||
* @param imageType The type of image.
|
||||
* @param paths The source paths for the image.
|
||||
* @param md5Hash The md5 hash or null.
|
||||
* @param sha1Hash The sha1 hash or null.
|
||||
* @param sha256Hash The sha256 hash or null.
|
||||
*/
|
||||
ContainerPanelData(DataSource dataSource, Long unallocatedFilesSize) {
|
||||
this.dataSource = dataSource;
|
||||
this.unallocatedFilesSize = unallocatedFilesSize;
|
||||
ImageViewModel(long unallocatedSize, long size, long sectorSize,
|
||||
String timeZone, String imageType, List<String> paths, String md5Hash,
|
||||
String sha1Hash, String sha256Hash) {
|
||||
this.unallocatedSize = unallocatedSize;
|
||||
this.size = size;
|
||||
this.sectorSize = sectorSize;
|
||||
this.timeZone = timeZone;
|
||||
this.imageType = imageType;
|
||||
this.paths = paths == null ? Collections.emptyList() : new ArrayList<>(paths);
|
||||
this.md5Hash = md5Hash;
|
||||
this.sha1Hash = sha1Hash;
|
||||
this.sha256Hash = sha256Hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The original datasource.
|
||||
* @return Size in bytes of unallocated space.
|
||||
*/
|
||||
DataSource getDataSource() {
|
||||
return dataSource;
|
||||
long getUnallocatedSize() {
|
||||
return unallocatedSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The unallocated file size.
|
||||
* @return Total size in bytes.
|
||||
*/
|
||||
Long getUnallocatedFilesSize() {
|
||||
return unallocatedFilesSize;
|
||||
long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Sector size in bytes.
|
||||
*/
|
||||
long getSectorSize() {
|
||||
return sectorSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The time zone.
|
||||
*/
|
||||
String getTimeZone() {
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The type of image.
|
||||
*/
|
||||
String getImageType() {
|
||||
return imageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The source paths for the image.
|
||||
*/
|
||||
List<String> getPaths() {
|
||||
return paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The md5 hash or null.
|
||||
*/
|
||||
String getMd5Hash() {
|
||||
return md5Hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The sha1 hash or null.
|
||||
*/
|
||||
String getSha1Hash() {
|
||||
return sha1Hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The sha256 hash or null.
|
||||
*/
|
||||
String getSha256Hash() {
|
||||
return sha256Hash;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* View model for container data.
|
||||
*/
|
||||
private static class ContainerViewModel {
|
||||
|
||||
private final String displayName;
|
||||
private final String originalName;
|
||||
private final String deviceIdValue;
|
||||
private final String acquisitionDetails;
|
||||
private final ImageViewModel imageViewModel;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param displayName The display name for this data source.
|
||||
* @param originalName The original name for this data source.
|
||||
* @param deviceIdValue The device id value for this data source.
|
||||
* @param acquisitionDetails The acquisition details for this data
|
||||
* source or null.
|
||||
* @param imageViewModel If the data source is an image, the image view
|
||||
* model for this data source or null if non-image.
|
||||
*/
|
||||
ContainerViewModel(String displayName, String originalName, String deviceIdValue,
|
||||
String acquisitionDetails, ImageViewModel imageViewModel) {
|
||||
this.displayName = displayName;
|
||||
this.originalName = originalName;
|
||||
this.deviceIdValue = deviceIdValue;
|
||||
this.acquisitionDetails = acquisitionDetails;
|
||||
this.imageViewModel = imageViewModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The display name for this data source.
|
||||
*/
|
||||
String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The original name for this data source.
|
||||
*/
|
||||
String getOriginalName() {
|
||||
return originalName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The device id value for this data source.
|
||||
*/
|
||||
String getDeviceId() {
|
||||
return deviceIdValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The acquisition details for this data source or null.
|
||||
*/
|
||||
String getAcquisitionDetails() {
|
||||
return acquisitionDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If the data source is an image, the image view model for this
|
||||
* data source or null if non-image.
|
||||
*/
|
||||
ImageViewModel getImageViewModel() {
|
||||
return imageViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,6 +262,7 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
private static final Logger logger = Logger.getLogger(ContainerPanel.class.getName());
|
||||
|
||||
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
|
||||
private final DataFetcher<DataSource, ContainerViewModel> containerDataFetcher;
|
||||
|
||||
/**
|
||||
* Creates a new form ContainerPanel.
|
||||
@ -117,21 +277,15 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
ContainerPanel(ContainerSummary containerSummary) {
|
||||
super(containerSummary, CONTAINER_UPDATES);
|
||||
|
||||
containerDataFetcher = (dataSource) -> getContainerViewModel(containerSummary, dataSource);
|
||||
|
||||
dataFetchComponents = Arrays.asList(
|
||||
new DataFetchComponents<>(
|
||||
(dataSource) -> {
|
||||
return new ContainerPanelData(
|
||||
dataSource,
|
||||
containerSummary.getSizeOfUnallocatedFiles(dataSource)
|
||||
);
|
||||
},
|
||||
containerDataFetcher,
|
||||
(result) -> {
|
||||
if (result != null && result.getResultType() == ResultType.SUCCESS) {
|
||||
ContainerPanelData data = result.getData();
|
||||
DataSource dataSource = (data == null) ? null : data.getDataSource();
|
||||
Long unallocatedFileSize = (data == null) ? null : data.getUnallocatedFilesSize();
|
||||
|
||||
updateDetailsPanelData(dataSource, unallocatedFileSize);
|
||||
ContainerViewModel data = result.getData();
|
||||
updateDetailsPanelData(data);
|
||||
} else {
|
||||
if (result == null) {
|
||||
logger.log(Level.WARNING, "No data fetch result was provided to the ContainerPanel.");
|
||||
@ -139,8 +293,7 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
logger.log(Level.WARNING, "An exception occurred while attempting to fetch data for the ContainerPanel.",
|
||||
result.getException());
|
||||
}
|
||||
|
||||
updateDetailsPanelData(null, null);
|
||||
updateDetailsPanelData(null);
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -161,33 +314,113 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update which DataSource this panel should display details about
|
||||
*
|
||||
* @param selectedDataSource the DataSource to display details about.
|
||||
* A means of retrieving data that could potentially throw an exception.
|
||||
*/
|
||||
private void updateDetailsPanelData(DataSource selectedDataSource, Long unallocatedFilesSize) {
|
||||
clearTableValues();
|
||||
if (selectedDataSource != null) {
|
||||
displayNameValue.setText(selectedDataSource.getName());
|
||||
originalNameValue.setText(selectedDataSource.getName());
|
||||
deviceIdValue.setText(selectedDataSource.getDeviceId());
|
||||
private interface Retriever<O> {
|
||||
|
||||
try {
|
||||
acquisitionDetailsTextArea.setText(selectedDataSource.getAcquisitionDetails());
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get acquisition details for selected data source", ex);
|
||||
/**
|
||||
* Retrieves data of a certain type and possibly throws an exception.
|
||||
*
|
||||
* @return The data type.
|
||||
* @throws TskCoreException
|
||||
* @throws SleuthkitCaseProviderException
|
||||
* @throws SQLException
|
||||
*/
|
||||
O retrieve() throws TskCoreException, SleuthkitCaseProviderException, SQLException;
|
||||
}
|
||||
|
||||
if (selectedDataSource instanceof Image) {
|
||||
setFieldsForImage((Image) selectedDataSource, unallocatedFilesSize);
|
||||
/**
|
||||
* Retrieves data of a particular type and handles any exceptions that may
|
||||
* be thrown by logging.
|
||||
*
|
||||
* @param retriever The retrieving function.
|
||||
* @return The retrieved data.
|
||||
*/
|
||||
private static <O> O retrieve(Retriever<O> retriever) {
|
||||
try {
|
||||
return retriever.retrieve();
|
||||
} catch (TskCoreException | SleuthkitCaseProviderException | SQLException ex) {
|
||||
logger.log(Level.WARNING, "Error while retrieving data.", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a container view model object containing data to display about
|
||||
* the data source.
|
||||
*
|
||||
* @param containerSummary The service providing data about the data source.
|
||||
* @param ds The data source.
|
||||
* @return The generated view model.
|
||||
*/
|
||||
private static ContainerViewModel getContainerViewModel(ContainerSummary containerSummary, DataSource ds) {
|
||||
if (ds == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ContainerViewModel(
|
||||
ds.getName(),
|
||||
ds.getName(),
|
||||
ds.getDeviceId(),
|
||||
retrieve(() -> ds.getAcquisitionDetails()),
|
||||
ds instanceof Image ? getImageViewModel(containerSummary, (Image) ds) : null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an image view model object containing data to display about the
|
||||
* image.
|
||||
*
|
||||
* @param containerSummary The service providing data about the image.
|
||||
* @param image The image.
|
||||
* @return The generated view model.
|
||||
*/
|
||||
private static ImageViewModel getImageViewModel(ContainerSummary containerSummary, Image image) {
|
||||
if (image == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Long unallocSize = retrieve(() -> containerSummary.getSizeOfUnallocatedFiles(image));
|
||||
String imageType = image.getType().getName();
|
||||
Long size = image.getSize();
|
||||
Long sectorSize = image.getSsize();
|
||||
String timeZone = image.getTimeZone();
|
||||
List<String> paths = image.getPaths() == null ? Collections.emptyList() : Arrays.asList(image.getPaths());
|
||||
String md5 = retrieve(() -> image.getMd5());
|
||||
String sha1 = retrieve(() -> image.getSha1());
|
||||
String sha256 = retrieve(() -> image.getSha256());
|
||||
|
||||
return new ImageViewModel(unallocSize, size, sectorSize, timeZone, imageType, paths, md5, sha1, sha256);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the swing components with fetched data.
|
||||
*
|
||||
* @param viewModel The data source view model data.
|
||||
*/
|
||||
private void updateDetailsPanelData(ContainerViewModel viewModel) {
|
||||
clearTableValues();
|
||||
if (viewModel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
displayNameValue.setText(viewModel.getDisplayName());
|
||||
originalNameValue.setText(viewModel.getOriginalName());
|
||||
deviceIdValue.setText(viewModel.getDeviceId());
|
||||
acquisitionDetailsTextArea.setText(viewModel.getAcquisitionDetails());
|
||||
|
||||
if (viewModel.getImageViewModel() != null) {
|
||||
setFieldsForImage(viewModel.getImageViewModel());
|
||||
} else {
|
||||
setFieldsForNonImageDataSource();
|
||||
}
|
||||
}
|
||||
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets image-only fields to N/A.
|
||||
*/
|
||||
@Messages({
|
||||
"ContainerPanel_setFieldsForNonImageDataSource_na=N/A"
|
||||
})
|
||||
@ -208,54 +441,24 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets text fields for an image. This should be called after
|
||||
* clearTableValues and before updateFieldVisibility to ensure the proper
|
||||
* rendering.
|
||||
* Sets fields for images.
|
||||
*
|
||||
* @param selectedImage The selected image.
|
||||
* @param unallocatedFilesSize Unallocated file size in bytes.
|
||||
* @param viewModel The image view model data.
|
||||
*/
|
||||
private void setFieldsForImage(Image selectedImage, Long unallocatedFilesSize) {
|
||||
unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(unallocatedFilesSize));
|
||||
imageTypeValue.setText(selectedImage.getType().getName());
|
||||
sizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSize()));
|
||||
sectorSizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSsize()));
|
||||
timeZoneValue.setText(selectedImage.getTimeZone());
|
||||
private void setFieldsForImage(ImageViewModel viewModel) {
|
||||
unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getUnallocatedSize()));
|
||||
imageTypeValue.setText(viewModel.getImageType());
|
||||
sizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getSize()));
|
||||
sectorSizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getSectorSize()));
|
||||
timeZoneValue.setText(viewModel.getTimeZone());
|
||||
|
||||
for (String path : selectedImage.getPaths()) {
|
||||
for (String path : viewModel.getPaths()) {
|
||||
((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path});
|
||||
}
|
||||
|
||||
try {
|
||||
//older databases may have null as the hash values
|
||||
String md5String = selectedImage.getMd5();
|
||||
if (md5String == null) {
|
||||
md5String = "";
|
||||
}
|
||||
md5HashValue.setText(md5String);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get MD5 for selected data source", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
String sha1String = selectedImage.getSha1();
|
||||
if (sha1String == null) {
|
||||
sha1String = "";
|
||||
}
|
||||
sha1HashValue.setText(sha1String);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get SHA1 for selected data source", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
String sha256String = selectedImage.getSha256();
|
||||
if (sha256String == null) {
|
||||
sha256String = "";
|
||||
}
|
||||
sha256HashValue.setText(sha256String);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get SHA256 for selected data source", ex);
|
||||
}
|
||||
md5HashValue.setText(viewModel.getMd5Hash());
|
||||
sha1HashValue.setText(viewModel.getSha1Hash());
|
||||
sha256HashValue.setText(viewModel.getSha256Hash());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -277,9 +480,82 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
((DefaultTableModel) filePathsTable.getModel()).setRowCount(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
List<ExcelExport.ExcelSheetExport> getExports(DataSource ds) {
|
||||
/**
|
||||
* Divides acquisition details into key/value pairs to be displayed in
|
||||
* separate cells in an excel export.
|
||||
*
|
||||
* @param acquisitionDetails The acquisition details.
|
||||
* @return The list of key value pairs that can be incorporated into the
|
||||
* excel export.
|
||||
*/
|
||||
private static List<? extends ExcelItemExportable> getAcquisitionDetails(String acquisitionDetails) {
|
||||
if (StringUtils.isBlank(acquisitionDetails)) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
return Stream.of(acquisitionDetails.split("\\r?\\n"))
|
||||
.map((line) -> (StringUtils.isBlank(line)) ? null : new SingleCellExportable(line))
|
||||
.filter(item -> item != null)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Messages({
|
||||
"ContainerPanel_export_displayName=Display Name:",
|
||||
"ContainerPanel_export_originalName=Name:",
|
||||
"ContainerPanel_export_deviceId=Device ID:",
|
||||
"ContainerPanel_export_timeZone=Time Zone:",
|
||||
"ContainerPanel_export_acquisitionDetails=Acquisition Details:",
|
||||
"ContainerPanel_export_imageType=Image Type:",
|
||||
"ContainerPanel_export_size=Size:",
|
||||
"ContainerPanel_export_sectorSize=Sector Size:",
|
||||
"ContainerPanel_export_md5=MD5:",
|
||||
"ContainerPanel_export_sha1=SHA1:",
|
||||
"ContainerPanel_export_sha256=SHA256:",
|
||||
"ContainerPanel_export_unallocatedSize=Unallocated Space:",
|
||||
"ContainerPanel_export_filePaths=File Paths:",})
|
||||
protected List<ExcelSheetExport> getExports(DataSource ds) {
|
||||
ContainerViewModel result = getFetchResult(containerDataFetcher, "Container sheets", ds);
|
||||
if (ds == null || result == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String NA = Bundle.ContainerPanel_setFieldsForNonImageDataSource_na();
|
||||
DefaultCellModel<?> NACell = new DefaultCellModel<>(NA);
|
||||
|
||||
ImageViewModel imageModel = result.getImageViewModel();
|
||||
boolean hasImage = imageModel != null;
|
||||
|
||||
DefaultCellModel<?> timeZone = hasImage ? new DefaultCellModel<>(imageModel.getTimeZone()) : NACell;
|
||||
DefaultCellModel<?> imageType = hasImage ? new DefaultCellModel<>(imageModel.getImageType()) : NACell;
|
||||
DefaultCellModel<?> size = hasImage ? SizeRepresentationUtil.getBytesCell(imageModel.getSize()) : NACell;
|
||||
DefaultCellModel<?> sectorSize = hasImage ? SizeRepresentationUtil.getBytesCell(imageModel.getSectorSize()) : NACell;
|
||||
DefaultCellModel<?> md5 = hasImage ? new DefaultCellModel<>(imageModel.getMd5Hash()) : NACell;
|
||||
DefaultCellModel<?> sha1 = hasImage ? new DefaultCellModel<>(imageModel.getSha1Hash()) : NACell;
|
||||
DefaultCellModel<?> sha256 = hasImage ? new DefaultCellModel<>(imageModel.getSha256Hash()) : NACell;
|
||||
DefaultCellModel<?> unallocatedSize = hasImage ? SizeRepresentationUtil.getBytesCell(imageModel.getUnallocatedSize()) : NACell;
|
||||
List<String> paths = result.getImageViewModel() == null ? Collections.singletonList(NA) : result.getImageViewModel().getPaths();
|
||||
List<SingleCellExportable> cellPaths = paths.stream()
|
||||
.map(SingleCellExportable::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Arrays.asList(
|
||||
new ExcelSpecialFormatExport(Bundle.ContainerPanel_tabName(), Arrays.asList(
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_displayName(), new DefaultCellModel<>(result.getDisplayName())),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_originalName(), new DefaultCellModel<>(result.getOriginalName())),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_deviceId(), new DefaultCellModel<>(result.getDeviceId())),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_timeZone(), timeZone),
|
||||
new TitledExportable(Bundle.ContainerPanel_export_acquisitionDetails(), getAcquisitionDetails(result.getAcquisitionDetails())),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_imageType(), imageType),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_size(), size),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_sectorSize(), sectorSize),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_md5(), md5),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_sha1(), sha1),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_sha256(), sha256),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_unallocatedSize(), unallocatedSize),
|
||||
new TitledExportable(Bundle.ContainerPanel_export_filePaths(), cellPaths)
|
||||
)));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,7 +68,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
*
|
||||
* @param tabTitle The title of the tab.
|
||||
* @param panel The component to be displayed in the tab.
|
||||
* @param notifyParentClose Notifies parent to trigger a close.
|
||||
*/
|
||||
DataSourceTab(String tabTitle, BaseDataSourceSummaryPanel panel) {
|
||||
this(tabTitle, panel, panel::setDataSource, panel::getExports, panel::close);
|
||||
@ -155,7 +154,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(),
|
||||
ingestHistoryPanel,
|
||||
ingestHistoryPanel::setDataSource,
|
||||
null,
|
||||
IngestJobExcelExport::getExports,
|
||||
null),
|
||||
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel()),
|
||||
new DataSourceTab(
|
||||
@ -270,7 +269,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), caseEventsListener);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
|
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.IngestJobInfo;
|
||||
import org.sleuthkit.datamodel.IngestModuleInfo;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Class that handles exporting information in IngestJobInfoPanel to excel.
|
||||
*/
|
||||
@Messages({
|
||||
"IngestJobExcelExport_startTimeColumn=Start Time",
|
||||
"IngestJobExcelExport_endTimeColumn=End Time",
|
||||
"IngestJobExcelExport_ingestStatusTimeColumn=Ingest Status",
|
||||
"IngestJobExcelExport_moduleNameTimeColumn=Module Name",
|
||||
"IngestJobExcelExport_versionColumn=Module Version",
|
||||
"IngestJobExcelExport_sheetName=Ingest History"
|
||||
})
|
||||
class IngestJobExcelExport {
|
||||
|
||||
/**
|
||||
* An entry to display in an excel export.
|
||||
*/
|
||||
private static class IngestJobEntry {
|
||||
|
||||
private final Date startTime;
|
||||
private final Date endTime;
|
||||
private final String status;
|
||||
private final String ingestModule;
|
||||
private final String ingestModuleVersion;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param startTime The ingest start time.
|
||||
* @param endTime The ingest stop time.
|
||||
* @param status The ingest status.
|
||||
* @param ingestModule The ingest module.
|
||||
* @param ingestModuleVersion The ingest module version.
|
||||
*/
|
||||
IngestJobEntry(Date startTime, Date endTime, String status, String ingestModule, String ingestModuleVersion) {
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
this.status = status;
|
||||
this.ingestModule = ingestModule;
|
||||
this.ingestModuleVersion = ingestModuleVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The ingest start time.
|
||||
*/
|
||||
Date getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The ingest stop time.
|
||||
*/
|
||||
Date getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The ingest status.
|
||||
*/
|
||||
String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The ingest module.
|
||||
*/
|
||||
String getIngestModule() {
|
||||
return ingestModule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The ingest module version.
|
||||
*/
|
||||
String getIngestModuleVersion() {
|
||||
return ingestModuleVersion;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger logger = Logger.getLogger(IngestJobExcelExport.class.getName());
|
||||
private static final String DATETIME_FORMAT_STR = "yyyy/MM/dd HH:mm:ss";
|
||||
private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat(DATETIME_FORMAT_STR, Locale.getDefault());
|
||||
|
||||
// columns in the excel export table to be created.
|
||||
private static final List<ColumnModel<IngestJobEntry, DefaultCellModel<?>>> COLUMNS = Arrays.asList(
|
||||
new ColumnModel<>(
|
||||
Bundle.IngestJobExcelExport_startTimeColumn(),
|
||||
(entry) -> getDateCell(entry.getStartTime())),
|
||||
new ColumnModel<>(
|
||||
Bundle.IngestJobExcelExport_endTimeColumn(),
|
||||
(entry) -> getDateCell(entry.getEndTime())),
|
||||
new ColumnModel<>(
|
||||
Bundle.IngestJobExcelExport_ingestStatusTimeColumn(),
|
||||
(entry) -> new DefaultCellModel<>(entry.getStatus())),
|
||||
new ColumnModel<>(
|
||||
Bundle.IngestJobExcelExport_moduleNameTimeColumn(),
|
||||
(entry) -> new DefaultCellModel<>(entry.getIngestModule())),
|
||||
new ColumnModel<>(
|
||||
Bundle.IngestJobExcelExport_versionColumn(),
|
||||
(entry) -> new DefaultCellModel<>(entry.getIngestModuleVersion()))
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieves data for a date cell.
|
||||
*
|
||||
* @param date The date.
|
||||
* @return The data cell to be used in the excel export.
|
||||
*/
|
||||
private static DefaultCellModel<?> getDateCell(Date date) {
|
||||
Function<Date, String> dateParser = (dt) -> dt == null ? "" : DATETIME_FORMAT.format(dt);
|
||||
return new DefaultCellModel<>(date, dateParser, DATETIME_FORMAT_STR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all the ingest job modules and versions for a job.
|
||||
*
|
||||
* @param job The ingest job.
|
||||
* @return All of the corresponding entries sorted by module name.
|
||||
*/
|
||||
private static List<IngestJobEntry> getEntries(IngestJobInfo job) {
|
||||
List<IngestModuleInfo> infoList = job.getIngestModuleInfo();
|
||||
if (infoList == null) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
Date startTime = job.getStartDateTime();
|
||||
Date endTime = job.getEndDateTime();
|
||||
String status = job.getStatus().getDisplayName();
|
||||
|
||||
return infoList.stream()
|
||||
.filter(info -> info != null)
|
||||
.map(info -> new IngestJobEntry(startTime, endTime, status, info.getDisplayName(), info.getVersion()))
|
||||
.sorted((a, b) -> {
|
||||
boolean aIsNull = a == null || a.getIngestModule() == null;
|
||||
boolean bIsNull = b == null || b.getIngestModule() == null;
|
||||
if (aIsNull || bIsNull) {
|
||||
return Boolean.compare(aIsNull, bIsNull);
|
||||
} else {
|
||||
return a.getIngestModule().compareTo(b.getIngestModule());
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For output, show ingest job details in first row present. Otherwise, set
|
||||
* to null.
|
||||
*
|
||||
* @param list The list of entries for an ingest job.
|
||||
* @return The stream of entries to be displayed.
|
||||
*/
|
||||
private static Stream<IngestJobEntry> showFirstRowOnly(List<IngestJobEntry> list) {
|
||||
return IntStream.range(0, list.size())
|
||||
.mapToObj(idx -> {
|
||||
IngestJobEntry entry = list.get(idx);
|
||||
if (entry == null || idx == 0) {
|
||||
return entry;
|
||||
} else {
|
||||
return new IngestJobEntry(null, null, null, entry.getIngestModule(), entry.getIngestModuleVersion());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of sheets to be exported for the Ingest History tab.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @return The list of sheets to be included in an export.
|
||||
*/
|
||||
static List<ExcelSheetExport> getExports(DataSource dataSource) {
|
||||
if (dataSource == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<IngestJobInfo> info = null;
|
||||
try {
|
||||
info = Case.getCurrentCaseThrows().getSleuthkitCase().getIngestJobs();
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "There was an error fetching ingest jobs", ex);
|
||||
}
|
||||
|
||||
if (info == null) {
|
||||
info = Collections.emptyList();
|
||||
}
|
||||
|
||||
List<IngestJobEntry> toDisplay = info.stream()
|
||||
.filter(job -> job != null && dataSource.getId() == job.getObjectId())
|
||||
.sorted((a, b) -> {
|
||||
// sort ingest jobs by time.
|
||||
boolean aIsNull = a.getStartDateTime() == null;
|
||||
boolean bIsNull = b.getStartDateTime() == null;
|
||||
if (aIsNull || bIsNull) {
|
||||
return Boolean.compare(aIsNull, bIsNull);
|
||||
} else {
|
||||
return a.getStartDateTime().compareTo(b.getStartDateTime());
|
||||
}
|
||||
})
|
||||
.map((job) -> getEntries(job))
|
||||
.filter(lst -> lst != null)
|
||||
.flatMap((lst) -> showFirstRowOnly(lst))
|
||||
.filter(item -> item != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Arrays.asList(new ExcelTableExport<>(Bundle.IngestJobExcelExport_sheetName(), COLUMNS, toDisplay));
|
||||
}
|
||||
|
||||
private IngestJobExcelExport() {
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ import java.text.DecimalFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
|
||||
/**
|
||||
* This class provides utilities for representing storage size in most relevant
|
||||
@ -32,14 +33,64 @@ public final class SizeRepresentationUtil {
|
||||
private static final int SIZE_CONVERSION_CONSTANT = 1000;
|
||||
private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##");
|
||||
|
||||
private static List<String> UNITS = Arrays.asList(
|
||||
Bundle.SizeRepresentationUtil_units_bytes(),
|
||||
Bundle.SizeRepresentationUtil_units_kilobytes(),
|
||||
Bundle.SizeRepresentationUtil_units_megabytes(),
|
||||
Bundle.SizeRepresentationUtil_units_gigabytes(),
|
||||
Bundle.SizeRepresentationUtil_units_terabytes(),
|
||||
Bundle.SizeRepresentationUtil_units_petabytes()
|
||||
);
|
||||
/**
|
||||
* A size unit corresponding to orders of magnitude of bytes (kilobyte, gigabytes, etc.).
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"SizeRepresentationUtil_units_bytes=bytes",
|
||||
"SizeRepresentationUtil_units_kilobytes=KB",
|
||||
"SizeRepresentationUtil_units_megabytes=MB",
|
||||
"SizeRepresentationUtil_units_gigabytes=GB",
|
||||
"SizeRepresentationUtil_units_terabytes=TB",
|
||||
"SizeRepresentationUtil_units_petabytes=PB"
|
||||
})
|
||||
enum SizeUnit {
|
||||
BYTES(Bundle.SizeRepresentationUtil_units_bytes(), "#", 0),
|
||||
KB(Bundle.SizeRepresentationUtil_units_kilobytes(), "#,##0.00,", 1),
|
||||
MB(Bundle.SizeRepresentationUtil_units_megabytes(), "#,##0.00,,", 2),
|
||||
GB(Bundle.SizeRepresentationUtil_units_gigabytes(), "#,##0.00,,,", 3),
|
||||
TB(Bundle.SizeRepresentationUtil_units_terabytes(), "#,##0.00,,,,", 4),
|
||||
PB(Bundle.SizeRepresentationUtil_units_petabytes(), "#,##0.00,,,,,", 5);
|
||||
|
||||
private final String suffix;
|
||||
private final String excelFormatString;
|
||||
private final long divisor;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
* @param suffix The string suffix to use for size unit.
|
||||
* @param excelFormatString The excel format string to use for this size unit.
|
||||
* @param power The power of 1000 of bytes for this size unit.
|
||||
*/
|
||||
SizeUnit(String suffix, String excelFormatString, int power) {
|
||||
this.suffix = suffix;
|
||||
|
||||
// based on https://www.mrexcel.com/board/threads/how-do-i-format-cells-to-show-gb-mb-kb.140135/
|
||||
this.excelFormatString = String.format("%s \"%s\"", excelFormatString, suffix);
|
||||
this.divisor = (long) Math.pow(SIZE_CONVERSION_CONSTANT, power);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The string suffix to use for size unit.
|
||||
*/
|
||||
public String getSuffix() {
|
||||
return suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The excel format string to use for this size unit.
|
||||
*/
|
||||
public String getExcelFormatString() {
|
||||
return excelFormatString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The divisor to convert from bytes to this unit.
|
||||
*/
|
||||
public long getDivisor() {
|
||||
return divisor;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a long size in bytes as a string formated to be read by users.
|
||||
@ -49,10 +100,31 @@ public final class SizeRepresentationUtil {
|
||||
* @return Return a string formated with a user friendly version of the size
|
||||
* as a string, returns empty String when provided empty size.
|
||||
*/
|
||||
public static String getSizeString(Long size) {
|
||||
static String getSizeString(Long size) {
|
||||
return getSizeString(size, APPROXIMATE_SIZE_FORMAT, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the relevant size unit that should be used for a particular size.
|
||||
* @param size The size in bytes.
|
||||
* @return The relevant size unit.
|
||||
*/
|
||||
static SizeUnit getSizeUnit(Long size) {
|
||||
if (size == null) {
|
||||
return SizeUnit.values()[0];
|
||||
}
|
||||
|
||||
for (int unitsIndex = 0; unitsIndex < SizeUnit.values().length; unitsIndex++) {
|
||||
SizeUnit unit = SizeUnit.values()[unitsIndex];
|
||||
long result = size / unit.getDivisor();
|
||||
if (result < SIZE_CONVERSION_CONSTANT) {
|
||||
return unit;
|
||||
}
|
||||
}
|
||||
|
||||
return SizeUnit.values()[SizeUnit.values().length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a long size in bytes as a string formated to be read by users.
|
||||
*
|
||||
@ -64,32 +136,21 @@ public final class SizeRepresentationUtil {
|
||||
* @return Return a string formated with a user friendly version of the size
|
||||
* as a string, returns empty String when provided empty size.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"SizeRepresentationUtil_units_bytes= bytes",
|
||||
"SizeRepresentationUtil_units_kilobytes= kB",
|
||||
"SizeRepresentationUtil_units_megabytes= MB",
|
||||
"SizeRepresentationUtil_units_gigabytes= GB",
|
||||
"SizeRepresentationUtil_units_terabytes= TB",
|
||||
"SizeRepresentationUtil_units_petabytes= PB"
|
||||
})
|
||||
public static String getSizeString(Long size, DecimalFormat format, boolean showFullSize) {
|
||||
static String getSizeString(Long size, DecimalFormat format, boolean showFullSize) {
|
||||
if (size == null) {
|
||||
return "";
|
||||
}
|
||||
double approximateSize = size;
|
||||
int unitsIndex = 0;
|
||||
for (; unitsIndex < UNITS.size(); unitsIndex++) {
|
||||
if (approximateSize < SIZE_CONVERSION_CONSTANT) {
|
||||
break;
|
||||
} else {
|
||||
approximateSize /= SIZE_CONVERSION_CONSTANT;
|
||||
}
|
||||
|
||||
SizeUnit sizeUnit = getSizeUnit(size);
|
||||
if (sizeUnit == null) {
|
||||
sizeUnit = SizeUnit.BYTES;
|
||||
}
|
||||
|
||||
String fullSize = size + UNITS.get(0);
|
||||
String closestUnitSize = format.format(approximateSize) + UNITS.get(unitsIndex);
|
||||
String closestUnitSize = String.format("%s %s",
|
||||
format.format(((double) size) / sizeUnit.getDivisor()), sizeUnit.getSuffix());
|
||||
|
||||
if (unitsIndex == 0) {
|
||||
String fullSize = String.format("%d %s", size, SizeUnit.BYTES.getSuffix());
|
||||
if (sizeUnit.equals(SizeUnit.BYTES)) {
|
||||
return fullSize;
|
||||
} else if (showFullSize) {
|
||||
return String.format("%s (%s)", closestUnitSize, fullSize);
|
||||
@ -98,6 +159,24 @@ public final class SizeRepresentationUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a default cell model using size units.
|
||||
* @param bytes The number of bytes.
|
||||
* @return The default cell model.
|
||||
*/
|
||||
static DefaultCellModel<?> getBytesCell(Long bytes) {
|
||||
if (bytes == null) {
|
||||
return new DefaultCellModel<>("");
|
||||
} else {
|
||||
SizeUnit unit = SizeRepresentationUtil.getSizeUnit(bytes);
|
||||
if (unit == null) {
|
||||
unit = SizeUnit.BYTES;
|
||||
}
|
||||
|
||||
return new DefaultCellModel<Long>(bytes, SizeRepresentationUtil::getSizeString, unit.getExcelFormatString());
|
||||
}
|
||||
}
|
||||
|
||||
private SizeRepresentationUtil() {
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary;
|
||||
@ -40,13 +39,16 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.KeyValueItemExportable;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel.PieChartItem;
|
||||
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory;
|
||||
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
@ -72,7 +74,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
"TypesPanel_fileMimeTypesChart_notAnalyzed_title=Not Analyzed",
|
||||
"TypesPanel_usageLabel_title=Usage",
|
||||
"TypesPanel_osLabel_title=OS",
|
||||
"TypesPanel_sizeLabel_title=Size"})
|
||||
"TypesPanel_sizeLabel_title=Size",
|
||||
"TypesPanel_excelTabName=Types"})
|
||||
class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
/**
|
||||
@ -167,10 +170,9 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final DecimalFormat INTEGER_SIZE_FORMAT = new DecimalFormat("#");
|
||||
private static final DecimalFormat COMMA_FORMATTER = new DecimalFormat("#,###");
|
||||
private static final String FILE_TYPE_FACTORY = FileTypeIdModuleFactory.class.getCanonicalName();
|
||||
private static final String FILE_TYPE_MODULE_NAME = FileTypeIdModuleFactory.getModuleName();
|
||||
private static final Logger logger = Logger.getLogger(TypesPanel.class.getName());
|
||||
private static final String COMMA_FORMAT_STR = "#,###";
|
||||
|
||||
private static final DecimalFormat COMMA_FORMATTER = new DecimalFormat(COMMA_FORMAT_STR);
|
||||
|
||||
private static final Color IMAGES_COLOR = new Color(156, 39, 176);
|
||||
private static final Color VIDEOS_COLOR = Color.YELLOW;
|
||||
@ -191,6 +193,15 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_unknown_title(), new HashSet<>(Arrays.asList("application/octet-stream")), UNKNOWN_COLOR)
|
||||
);
|
||||
|
||||
private final DataFetcher<DataSource, String> usageFetcher;
|
||||
private final DataFetcher<DataSource, String> osFetcher;
|
||||
private final DataFetcher<DataSource, Long> sizeFetcher;
|
||||
|
||||
private final DataFetcher<DataSource, Long> allocatedFetcher;
|
||||
private final DataFetcher<DataSource, Long> unallocatedFetcher;
|
||||
private final DataFetcher<DataSource, Long> slackFetcher;
|
||||
private final DataFetcher<DataSource, Long> directoriesFetcher;
|
||||
|
||||
private final LoadableLabel usageLabel = new LoadableLabel(Bundle.TypesPanel_usageLabel_title());
|
||||
private final LoadableLabel osLabel = new LoadableLabel(Bundle.TypesPanel_osLabel_title());
|
||||
private final LoadableLabel sizeLabel = new LoadableLabel(Bundle.TypesPanel_sizeLabel_title());
|
||||
@ -246,42 +257,34 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
super(mimeTypeData, typeData, containerData);
|
||||
|
||||
this.usageFetcher = containerData::getDataSourceType;
|
||||
this.osFetcher = containerData::getOperatingSystems;
|
||||
|
||||
this.sizeFetcher = (dataSource) -> dataSource == null ? null : dataSource.getSize();
|
||||
|
||||
this.allocatedFetcher = (dataSource) -> typeData.getCountOfAllocatedFiles(dataSource);
|
||||
this.unallocatedFetcher = (dataSource) -> typeData.getCountOfUnallocatedFiles(dataSource);
|
||||
this.slackFetcher = (dataSource) -> typeData.getCountOfSlackFiles(dataSource);
|
||||
this.directoriesFetcher = (dataSource) -> typeData.getCountOfDirectories(dataSource);
|
||||
|
||||
this.dataFetchComponents = Arrays.asList(
|
||||
// usage label worker
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
containerData::getDataSourceType,
|
||||
(result) -> usageLabel.showDataFetchResult(result)),
|
||||
// os label worker
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
containerData::getOperatingSystems,
|
||||
(result) -> osLabel.showDataFetchResult(result)),
|
||||
// size label worker
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> {
|
||||
Long size = dataSource == null ? null : dataSource.getSize();
|
||||
return SizeRepresentationUtil.getSizeString(size, INTEGER_SIZE_FORMAT, false);
|
||||
},
|
||||
sizeLabel::showDataFetchResult),
|
||||
// file types worker
|
||||
new DataFetchWorker.DataFetchComponents<>(usageFetcher, usageLabel::showDataFetchResult),
|
||||
new DataFetchWorker.DataFetchComponents<>(osFetcher, osLabel::showDataFetchResult),
|
||||
new DataFetchWorker.DataFetchComponents<>(sizeFetcher,
|
||||
(sizeResult) -> sizeLabel.showDataFetchResult(
|
||||
DataFetchResult.getSubResult(sizeResult,
|
||||
size -> SizeRepresentationUtil.getSizeString(size, INTEGER_SIZE_FORMAT, false)))),
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> getMimeTypeCategoriesModel(mimeTypeData, dataSource),
|
||||
this::showMimeTypeCategories),
|
||||
// allocated files worker
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> getStringOrZero(typeData.getCountOfAllocatedFiles(dataSource)),
|
||||
allocatedLabel::showDataFetchResult),
|
||||
// unallocated files worker
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> getStringOrZero(typeData.getCountOfUnallocatedFiles(dataSource)),
|
||||
unallocatedLabel::showDataFetchResult),
|
||||
// slack files worker
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> getStringOrZero(typeData.getCountOfSlackFiles(dataSource)),
|
||||
slackLabel::showDataFetchResult),
|
||||
// directories worker
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> getStringOrZero(typeData.getCountOfDirectories(dataSource)),
|
||||
directoriesLabel::showDataFetchResult)
|
||||
new DataFetchWorker.DataFetchComponents<>(allocatedFetcher,
|
||||
countRes -> allocatedLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))),
|
||||
new DataFetchWorker.DataFetchComponents<>(unallocatedFetcher,
|
||||
countRes -> unallocatedLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))),
|
||||
new DataFetchWorker.DataFetchComponents<>(slackFetcher,
|
||||
countRes -> slackLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))),
|
||||
new DataFetchWorker.DataFetchComponents<>(directoriesFetcher,
|
||||
countRes -> directoriesLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count))))
|
||||
);
|
||||
|
||||
initComponents();
|
||||
@ -406,11 +409,55 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
return longVal == null ? "0" : COMMA_FORMATTER.format(longVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a key value pair to be exported in a sheet.
|
||||
*
|
||||
* @param fetcher The means of fetching the data.
|
||||
* @param key The key to use.
|
||||
* @param dataSource The data source containing the data.
|
||||
* @return The key value pair to be exported.
|
||||
*/
|
||||
private static KeyValueItemExportable getStrExportable(DataFetcher<DataSource, String> fetcher, String key, DataSource dataSource) {
|
||||
String result = getFetchResult(fetcher, "Types", dataSource);
|
||||
return (result == null) ? null : new KeyValueItemExportable(key, new DefaultCellModel<>(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a key value pair to be exported in a sheet formatting the long
|
||||
* with commas separated by orders of 1000.
|
||||
*
|
||||
* @param fetcher The means of fetching the data.
|
||||
* @param key The string key for this key value pair.
|
||||
* @param dataSource The data source.
|
||||
* @return The key value pair.
|
||||
*/
|
||||
private static KeyValueItemExportable getCountExportable(DataFetcher<DataSource, Long> fetcher, String key, DataSource dataSource) {
|
||||
Long count = getFetchResult(fetcher, "Types", dataSource);
|
||||
return (count == null) ? null : new KeyValueItemExportable(key,
|
||||
new DefaultCellModel<Long>(count, COMMA_FORMATTER::format, COMMA_FORMAT_STR));
|
||||
}
|
||||
|
||||
@Override
|
||||
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
|
||||
if (dataSource == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Arrays.asList(new ExcelSpecialFormatExport(Bundle.TypesPanel_excelTabName(),
|
||||
Stream.of(
|
||||
getStrExportable(usageFetcher, Bundle.TypesPanel_usageLabel_title(), dataSource),
|
||||
getStrExportable(osFetcher, Bundle.TypesPanel_osLabel_title(), dataSource),
|
||||
new KeyValueItemExportable(Bundle.TypesPanel_sizeLabel_title(),
|
||||
SizeRepresentationUtil.getBytesCell(getFetchResult(sizeFetcher, "Types", dataSource))),
|
||||
getCountExportable(allocatedFetcher, Bundle.TypesPanel_filesByCategoryTable_allocatedRow_title(), dataSource),
|
||||
getCountExportable(unallocatedFetcher, Bundle.TypesPanel_filesByCategoryTable_unallocatedRow_title(), dataSource),
|
||||
getCountExportable(slackFetcher, Bundle.TypesPanel_filesByCategoryTable_slackRow_title(), dataSource),
|
||||
getCountExportable(directoriesFetcher, Bundle.TypesPanel_filesByCategoryTable_directoryRow_title(), dataSource))
|
||||
.filter(sheet -> sheet != null)
|
||||
.collect(Collectors.toList())
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
|
||||
/**
|
||||
* Basic interface for a cell model.
|
||||
@ -29,20 +30,23 @@ public interface CellModel {
|
||||
* Describes the horizontal alignment.
|
||||
*/
|
||||
public enum HorizontalAlign {
|
||||
LEFT(JLabel.LEFT),
|
||||
CENTER(JLabel.CENTER),
|
||||
RIGHT(JLabel.RIGHT);
|
||||
LEFT(JLabel.LEFT, HorizontalAlignment.LEFT),
|
||||
CENTER(JLabel.CENTER, HorizontalAlignment.CENTER),
|
||||
RIGHT(JLabel.RIGHT, HorizontalAlignment.RIGHT);
|
||||
|
||||
private final int jlabelAlignment;
|
||||
private final HorizontalAlignment poiAlignment;
|
||||
|
||||
/**
|
||||
* Constructor for a HorizontalAlign enum.
|
||||
*
|
||||
* @param jlabelAlignment The corresponding JLabel horizontal alignment
|
||||
* number.
|
||||
* @param poiAlignment Horizontal alignment for Apache POI.
|
||||
*/
|
||||
HorizontalAlign(int jlabelAlignment) {
|
||||
HorizontalAlign(int jlabelAlignment, HorizontalAlignment poiAlignment) {
|
||||
this.jlabelAlignment = jlabelAlignment;
|
||||
this.poiAlignment = poiAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,6 +56,13 @@ public interface CellModel {
|
||||
int getJLabelAlignment() {
|
||||
return this.jlabelAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Horizontal alignment for Apache POI.
|
||||
*/
|
||||
HorizontalAlignment getPoiAlignment() {
|
||||
return poiAlignment;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,21 +24,21 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport.ExcelCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelCellModel;
|
||||
|
||||
/**
|
||||
* The default cell model.
|
||||
*/
|
||||
public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
|
||||
|
||||
private final T data;
|
||||
private final Function<T, String> stringConverter;
|
||||
final T data;
|
||||
final Function<T, String> stringConverter;
|
||||
String tooltip;
|
||||
CellModel.HorizontalAlign horizontalAlignment;
|
||||
Insets insets;
|
||||
List<MenuItem> popupMenu;
|
||||
Supplier<List<MenuItem>> menuItemSupplier;
|
||||
private final String excelFormatString;
|
||||
final String excelFormatString;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
@ -76,6 +76,7 @@ public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
|
||||
this.data = data;
|
||||
this.stringConverter = stringConverter;
|
||||
this.excelFormatString = excelFormatString;
|
||||
this.tooltip = getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
|
||||
/**
|
||||
* Basic interface for a cell model.
|
||||
*/
|
||||
public interface ExcelCellModel extends CellModel {
|
||||
|
||||
/**
|
||||
* @return The format string to be used with Apache POI during excel
|
||||
* export or null if none necessary.
|
||||
*/
|
||||
String getExcelFormatString();
|
||||
|
||||
}
|
@ -21,13 +21,23 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModel.HorizontalAlign;
|
||||
|
||||
/**
|
||||
* Class for handling Excel exporting.
|
||||
@ -59,6 +69,87 @@ public class ExcelExport {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cell style key that can be used with the WorksheetEnv to generate a
|
||||
* cell style to be used in a POI excel document.
|
||||
*/
|
||||
static class CellStyleKey {
|
||||
|
||||
private final String formatString;
|
||||
private final CellStyle cellStyle;
|
||||
private final HorizontalAlign alignment;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param formatString The format string or null if no special
|
||||
* formatting.
|
||||
* @param cellStyle The base cell style or null if default is to be
|
||||
* used.
|
||||
* @param alignment The horizontal alignment or null if default is to be
|
||||
* used.
|
||||
*/
|
||||
CellStyleKey(String formatString, CellStyle cellStyle, HorizontalAlign alignment) {
|
||||
this.formatString = formatString;
|
||||
this.cellStyle = cellStyle;
|
||||
this.alignment = alignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The format string or null if no special formatting.
|
||||
*/
|
||||
String getFormatString() {
|
||||
return formatString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The base cell style or null if default is to be used.
|
||||
*/
|
||||
CellStyle getCellStyle() {
|
||||
return cellStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The horizontal alignment or null if default is to be used.
|
||||
*/
|
||||
HorizontalAlign getAlignment() {
|
||||
return alignment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 29 * hash + Objects.hashCode(this.formatString);
|
||||
hash = 29 * hash + Objects.hashCode(this.cellStyle);
|
||||
hash = 29 * hash + Objects.hashCode(this.alignment);
|
||||
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 CellStyleKey other = (CellStyleKey) obj;
|
||||
if (!Objects.equals(this.formatString, other.formatString)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.cellStyle, other.cellStyle)) {
|
||||
return false;
|
||||
}
|
||||
if (this.alignment != other.alignment) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class detailing aspects of the worksheet.
|
||||
*/
|
||||
@ -66,18 +157,47 @@ public class ExcelExport {
|
||||
|
||||
private final CellStyle headerStyle;
|
||||
private final Workbook parentWorkbook;
|
||||
private final CellStyle defaultStyle;
|
||||
|
||||
// maps a data format string / original cell style combination to a created cell style
|
||||
private final Map<CellStyleKey, CellStyle> cellStyleCache = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param headerStyle The cell style to use for headers.
|
||||
* @param defaultStyle The cell style to use as a default.
|
||||
* @param parentWorkbook The parent workbook.
|
||||
*/
|
||||
WorksheetEnv(CellStyle headerStyle, Workbook parentWorkbook) {
|
||||
WorksheetEnv(CellStyle headerStyle, CellStyle defaultStyle, Workbook parentWorkbook) {
|
||||
this.headerStyle = headerStyle;
|
||||
this.defaultStyle = defaultStyle;
|
||||
this.parentWorkbook = parentWorkbook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cell style signified by the given cell style key. If the
|
||||
* key is already present, a cached version is returned.
|
||||
*
|
||||
* @param cellStyleKey The key.
|
||||
* @return The cell style representing this key.
|
||||
*/
|
||||
public CellStyle getCellStyle(CellStyleKey cellStyleKey) {
|
||||
return cellStyleCache.computeIfAbsent(cellStyleKey, (pair) -> {
|
||||
CellStyle computed = this.parentWorkbook.createCellStyle();
|
||||
computed.cloneStyleFrom(cellStyleKey.getCellStyle() == null ? defaultStyle : cellStyleKey.getCellStyle());
|
||||
|
||||
if (cellStyleKey.getAlignment() != null) {
|
||||
computed.setAlignment(cellStyleKey.getAlignment().getPoiAlignment());
|
||||
}
|
||||
|
||||
if (cellStyleKey.getFormatString() != null) {
|
||||
computed.setDataFormat(this.parentWorkbook.getCreationHelper().createDataFormat().getFormat(cellStyleKey.getFormatString()));
|
||||
}
|
||||
return computed;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cell style to use for headers.
|
||||
*
|
||||
@ -87,6 +207,15 @@ public class ExcelExport {
|
||||
return headerStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cell style for default items.
|
||||
*
|
||||
* @return The cell style for default items.
|
||||
*/
|
||||
public CellStyle getDefaultCellStyle() {
|
||||
return defaultStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent workbook.
|
||||
*
|
||||
@ -125,6 +254,7 @@ public class ExcelExport {
|
||||
|
||||
/**
|
||||
* Retrieves a singleton instance of this class.
|
||||
*
|
||||
* @return The instance.
|
||||
*/
|
||||
public static ExcelExport getInstance() {
|
||||
@ -141,6 +271,7 @@ public class ExcelExport {
|
||||
|
||||
/**
|
||||
* Writes the exports to a workbook.
|
||||
*
|
||||
* @param exports The sheets to export.
|
||||
* @param path The path to the output file.
|
||||
* @throws IOException
|
||||
@ -160,10 +291,15 @@ public class ExcelExport {
|
||||
//headerFont.setFontHeightInPoints((short) 14);
|
||||
|
||||
// Create a CellStyle with the font
|
||||
HorizontalAlignment alignment = HorizontalAlignment.LEFT;
|
||||
CellStyle headerCellStyle = workbook.createCellStyle();
|
||||
headerCellStyle.setFont(headerFont);
|
||||
headerCellStyle.setAlignment(alignment);
|
||||
|
||||
WorksheetEnv env = new WorksheetEnv(headerCellStyle, workbook);
|
||||
CellStyle defaultCellStyle = workbook.createCellStyle();
|
||||
defaultCellStyle.setAlignment(alignment);
|
||||
|
||||
WorksheetEnv env = new WorksheetEnv(headerCellStyle, defaultCellStyle, workbook);
|
||||
|
||||
if (exports != null) {
|
||||
for (int i = 0; i < exports.size(); i++) {
|
||||
@ -190,4 +326,46 @@ public class ExcelExport {
|
||||
// Closing the workbook
|
||||
workbook.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an excel cell given the model.
|
||||
*
|
||||
* @param env The work sheet environment including the workbook.
|
||||
* @param row The row in the excel document.
|
||||
* @param colNum The column number (not zero-indexed).
|
||||
* @param cellModel The model for the cell.
|
||||
* @param cellStyle The style to use.
|
||||
* @return The created cell.
|
||||
*/
|
||||
static Cell createCell(WorksheetEnv env, Row row, int colNum, ExcelCellModel cellModel, Optional<CellStyle> cellStyle) {
|
||||
CellStyle cellStyleToUse = cellStyle.orElse(env.getDefaultCellStyle());
|
||||
|
||||
if (cellModel.getExcelFormatString() != null || cellModel.getHorizontalAlignment() != null) {
|
||||
cellStyleToUse = env.getCellStyle(new CellStyleKey(cellModel.getExcelFormatString(), cellStyleToUse, cellModel.getHorizontalAlignment()));
|
||||
}
|
||||
|
||||
Object cellData = cellModel.getData();
|
||||
Cell cell = row.createCell(colNum);
|
||||
if (cellData instanceof Calendar) {
|
||||
cell.setCellValue((Calendar) cellData);
|
||||
} else if (cellData instanceof Date) {
|
||||
cell.setCellValue((Date) cellData);
|
||||
} else if (cellData instanceof Double) {
|
||||
cell.setCellValue((Double) cellData);
|
||||
} else if (cellData instanceof String) {
|
||||
cell.setCellValue((String) cellData);
|
||||
} else if (cellData instanceof Short) {
|
||||
cell.setCellValue((Short) cellData);
|
||||
} else if (cellData instanceof Integer) {
|
||||
cell.setCellValue((Integer) cellData);
|
||||
} else if (cellData instanceof Long) {
|
||||
cell.setCellValue((Long) cellData);
|
||||
} else if (cellData instanceof Float) {
|
||||
cell.setCellValue((Float) cellData);
|
||||
} else {
|
||||
cell.setCellValue(cellModel.getText());
|
||||
}
|
||||
cell.setCellStyle(cellStyleToUse);
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException;
|
||||
|
||||
/**
|
||||
* An excel export that has special row-by-row formatting.
|
||||
*/
|
||||
public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport {
|
||||
|
||||
/**
|
||||
* The dimensions consumed by an item in an ExcelSpecialFormatExport list of
|
||||
* items to be rendered.
|
||||
*/
|
||||
public static class ItemDimensions {
|
||||
|
||||
private final int rowStart;
|
||||
private final int rowEnd;
|
||||
private final int colStart;
|
||||
private final int colEnd;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param rowStart The starting excel row of the item.
|
||||
* @param colStart The starting excel column of the item.
|
||||
* @param rowEnd The last excel row of the the item.
|
||||
* @param colEnd The last excel column of the item.
|
||||
*/
|
||||
public ItemDimensions(int rowStart, int colStart, int rowEnd, int colEnd) {
|
||||
this.rowStart = rowStart;
|
||||
this.colStart = colStart;
|
||||
this.rowEnd = rowEnd;
|
||||
this.colEnd = colEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The starting excel row of the item.
|
||||
*/
|
||||
public int getRowStart() {
|
||||
return rowStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The last excel row of the the item.
|
||||
*/
|
||||
public int getRowEnd() {
|
||||
return rowEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The starting excel column of the item.
|
||||
*/
|
||||
public int getColStart() {
|
||||
return colStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The last excel column of the item.
|
||||
*/
|
||||
public int getColEnd() {
|
||||
return colEnd;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An item to be exported in a specially formatted excel export.
|
||||
*/
|
||||
public interface ExcelItemExportable {
|
||||
|
||||
/**
|
||||
* Writes the item to the sheet in the special format export sheet.
|
||||
*
|
||||
* @param sheet The sheet.
|
||||
* @param rowStart The starting row to start writing.
|
||||
* @param colStart The starting column to start writing.
|
||||
* @param env The excel export context.
|
||||
* @return The dimensions of what has been written.
|
||||
* @throws ExcelExportException
|
||||
*/
|
||||
ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env) throws ExcelExportException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string to a single cell in a specially formatted excel export.
|
||||
*/
|
||||
public static class SingleCellExportable implements ExcelItemExportable {
|
||||
|
||||
private final ExcelCellModel item;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param key The text to be written.
|
||||
*/
|
||||
public SingleCellExportable(String key) {
|
||||
this(new DefaultCellModel<>(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param item The cell model to be written.
|
||||
*/
|
||||
public SingleCellExportable(ExcelCellModel item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env) throws ExcelExportException {
|
||||
Row row = sheet.createRow(rowStart);
|
||||
ExcelExport.createCell(env, row, colStart, item, Optional.empty());
|
||||
return new ItemDimensions(rowStart, colStart, rowStart, colStart);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a row consisting of first column as a key and second column as a
|
||||
* value.
|
||||
*/
|
||||
public static class KeyValueItemExportable implements ExcelItemExportable {
|
||||
|
||||
private final ExcelCellModel key;
|
||||
private final ExcelCellModel value;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param key The string key to be exported.
|
||||
* @param value The cell model to be exported.
|
||||
*/
|
||||
public KeyValueItemExportable(String key, ExcelCellModel value) {
|
||||
this(new DefaultCellModel<>(key), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param key The cell key to be exported.
|
||||
* @param value The cell model to be exported.
|
||||
*/
|
||||
public KeyValueItemExportable(ExcelCellModel key, ExcelCellModel value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env) throws ExcelExportException {
|
||||
Row row = sheet.createRow(rowStart);
|
||||
ExcelExport.createCell(env, row, colStart, key, Optional.of(env.getHeaderStyle()));
|
||||
ExcelExport.createCell(env, row, colStart + 1, value, Optional.empty());
|
||||
return new ItemDimensions(rowStart, colStart, rowStart, colStart + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A special format excel export item that shows a title and a list of items
|
||||
* indented one column.
|
||||
*
|
||||
* i.e.
|
||||
* <pre>
|
||||
* title
|
||||
* item 1
|
||||
* item 2
|
||||
* </pre>
|
||||
*/
|
||||
public static class TitledExportable implements ExcelItemExportable {
|
||||
|
||||
private static final int DEFAULT_INDENT = 1;
|
||||
|
||||
private final String title;
|
||||
private final List<? extends ExcelItemExportable> children;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param title The title for the export.
|
||||
* @param children The children to be indented and enumerated.
|
||||
*/
|
||||
public TitledExportable(String title, List<? extends ExcelItemExportable> children) {
|
||||
this.title = title;
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env) throws ExcelExportException {
|
||||
ExcelExport.createCell(env, sheet.createRow(rowStart), colStart, new DefaultCellModel<>(title), Optional.of(env.getHeaderStyle()));
|
||||
int curRow = rowStart + 1;
|
||||
int maxCol = colStart;
|
||||
for (ExcelItemExportable export : children) {
|
||||
if (export == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ItemDimensions thisItemDim = export.write(sheet, curRow, colStart + DEFAULT_INDENT, env);
|
||||
curRow = thisItemDim.getRowEnd() + 1;
|
||||
maxCol = Math.max(thisItemDim.getColEnd(), maxCol);
|
||||
}
|
||||
|
||||
return new ItemDimensions(rowStart, colStart, curRow - 1, maxCol);
|
||||
}
|
||||
}
|
||||
|
||||
private final String sheetName;
|
||||
private final List<ExcelItemExportable> exports;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param sheetName The name of the sheet.
|
||||
* @param exports The row-by-row items to be exported.
|
||||
*/
|
||||
public ExcelSpecialFormatExport(String sheetName, List<ExcelItemExportable> exports) {
|
||||
this.sheetName = sheetName;
|
||||
this.exports = exports == null ? Collections.emptyList() : exports;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSheetName() {
|
||||
return sheetName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderSheet(Sheet sheet, ExcelExport.WorksheetEnv env) throws ExcelExportException {
|
||||
int rowStart = 0;
|
||||
int maxCol = 0;
|
||||
for (ExcelItemExportable export : exports) {
|
||||
if (export == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ItemDimensions dimensions = export.write(sheet, rowStart, 0, env);
|
||||
rowStart = dimensions.getRowEnd() + 1;
|
||||
maxCol = Math.max(maxCol, dimensions.getColEnd());
|
||||
}
|
||||
|
||||
// Resize all columns to fit the content size
|
||||
for (int i = 0; i <= maxCol; i++) {
|
||||
sheet.autoSizeColumn(i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -18,40 +18,26 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport.ExcelCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ExcelItemExportable;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ItemDimensions;
|
||||
|
||||
/**
|
||||
* An excel sheet export of table data.
|
||||
*/
|
||||
public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelExport.ExcelSheetExport {
|
||||
|
||||
/**
|
||||
* Basic interface for a cell model.
|
||||
*/
|
||||
public interface ExcelCellModel extends CellModel {
|
||||
|
||||
/**
|
||||
* @return The format string to be used with Apache POI during excel
|
||||
* export or null if none necessary.
|
||||
*/
|
||||
String getExcelFormatString();
|
||||
}
|
||||
public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelSheetExport, ExcelItemExportable {
|
||||
|
||||
private final String sheetName;
|
||||
private final List<ColumnModel<T, C>> columns;
|
||||
private final List<T> data;
|
||||
private final int columnIndent;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
@ -62,9 +48,23 @@ public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelExpor
|
||||
* @param data The data to export.
|
||||
*/
|
||||
public ExcelTableExport(String sheetName, List<ColumnModel<T, C>> columns, List<T> data) {
|
||||
this(sheetName, columns, data, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param sheetName The name of the sheet. NOTE: There can be no duplicates
|
||||
* in a workbook.
|
||||
* @param columns The columns of the table.
|
||||
* @param data The data to export.
|
||||
* @param columnIndent The column indent.
|
||||
*/
|
||||
public ExcelTableExport(String sheetName, List<ColumnModel<T, C>> columns, List<T> data, int columnIndent) {
|
||||
this.sheetName = sheetName;
|
||||
this.columns = columns;
|
||||
this.data = data;
|
||||
this.columnIndent = columnIndent;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -74,11 +74,20 @@ public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelExpor
|
||||
|
||||
@Override
|
||||
public void renderSheet(Sheet sheet, ExcelExport.WorksheetEnv style) throws ExcelExport.ExcelExportException {
|
||||
renderSheet(sheet, style, columns, data);
|
||||
renderSheet(sheet, style, 0, columnIndent, columns, data);
|
||||
|
||||
// Resize all columns to fit the content size
|
||||
for (int i = 0; i < columns.size(); i++) {
|
||||
sheet.autoSizeColumn(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env) throws ExcelExportException {
|
||||
int columnStart = columnIndent + colStart;
|
||||
int rowsWritten = renderSheet(sheet, env, rowStart, columnStart, columns, data);
|
||||
return new ItemDimensions(rowStart, columnStart, rowStart + rowsWritten - 1, this.columns == null ? columnStart : columnStart + this.columns.size());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,88 +95,44 @@ public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelExpor
|
||||
*
|
||||
* @param sheet The sheet.
|
||||
* @param worksheetEnv The worksheet environment and preferences.
|
||||
* @param rowStart The row to start in.
|
||||
* @param colStart The column to start in.
|
||||
* @param columns The columns.
|
||||
* @param data The data.
|
||||
* @throws ExcelExportException
|
||||
* @return The number of rows (including the header) written.
|
||||
*/
|
||||
private static <T, C extends ExcelCellModel> void renderSheet(
|
||||
Sheet sheet, ExcelExport.WorksheetEnv worksheetEnv, List<ColumnModel<T, C>> columns, List<T> data)
|
||||
private static <T, C extends ExcelCellModel> int renderSheet(
|
||||
Sheet sheet,
|
||||
ExcelExport.WorksheetEnv worksheetEnv,
|
||||
int rowStart,
|
||||
int colStart,
|
||||
List<ColumnModel<T, C>> columns, List<T> data)
|
||||
throws ExcelExport.ExcelExportException {
|
||||
|
||||
List<T> safeData = data == null ? Collections.emptyList() : data;
|
||||
// Create a header row
|
||||
Row headerRow = sheet.createRow(0);
|
||||
Row headerRow = sheet.createRow(rowStart);
|
||||
// Create header cells
|
||||
for (int i = 0; i < columns.size(); i++) {
|
||||
Cell cell = headerRow.createCell(i);
|
||||
Cell cell = headerRow.createCell(i + colStart);
|
||||
cell.setCellValue(columns.get(i).getHeaderTitle());
|
||||
cell.setCellStyle(worksheetEnv.getHeaderStyle());
|
||||
}
|
||||
// freeze header row
|
||||
sheet.createFreezePane(0, 1);
|
||||
// Create Cell Style for each column (if one is needed)
|
||||
Map<String, CellStyle> cellStyles = new HashMap<>();
|
||||
|
||||
for (int rowNum = 0; rowNum < safeData.size(); rowNum++) {
|
||||
T rowData = safeData.get(rowNum);
|
||||
Row row = sheet.createRow(rowNum + 1);
|
||||
Row row = sheet.createRow(rowNum + rowStart + 1);
|
||||
for (int colNum = 0; colNum < columns.size(); colNum++) {
|
||||
ColumnModel<T, ? extends ExcelCellModel> colModel = columns.get(colNum);
|
||||
ExcelCellModel cellModel = colModel.getCellRenderer().apply(rowData);
|
||||
String formatString = cellModel.getExcelFormatString();
|
||||
Optional<CellStyle> cellStyle = (formatString == null)
|
||||
? Optional.empty()
|
||||
: Optional.of(cellStyles.computeIfAbsent(formatString, k -> createCellStyle(worksheetEnv.getParentWorkbook(), formatString)));
|
||||
createCell(row, colNum, cellModel, cellStyle);
|
||||
}
|
||||
ExcelExport.createCell(worksheetEnv, row, colNum + colStart, cellModel, Optional.empty());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a cell style in the workbook with the given format string.
|
||||
*
|
||||
* @param workbook The workbook.
|
||||
* @param formatString The format string.
|
||||
* @return The cell style.
|
||||
*/
|
||||
private static <T> CellStyle createCellStyle(Workbook workbook, String formatString) {
|
||||
CellStyle cellStyle = workbook.createCellStyle();
|
||||
cellStyle.setDataFormat(workbook.getCreationHelper().createDataFormat().getFormat(formatString));
|
||||
return cellStyle;
|
||||
return safeData.size() + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an excel cell given the model.
|
||||
*
|
||||
* @param row The row in the excel document.
|
||||
* @param colNum The column number (not zero-indexed).
|
||||
* @param cellModel The model for the cell.
|
||||
* @param cellStyle The style to use.
|
||||
* @return The created cell.
|
||||
*/
|
||||
private static Cell createCell(Row row, int colNum, ExcelCellModel cellModel, Optional<CellStyle> cellStyle) {
|
||||
Object cellData = cellModel.getData();
|
||||
Cell cell = row.createCell(colNum);
|
||||
if (cellData instanceof Calendar) {
|
||||
cell.setCellValue((Calendar) cellData);
|
||||
} else if (cellData instanceof Date) {
|
||||
cell.setCellValue((Date) cellData);
|
||||
} else if (cellData instanceof Double) {
|
||||
cell.setCellValue((Double) cellData);
|
||||
} else if (cellData instanceof String) {
|
||||
cell.setCellValue((String) cellData);
|
||||
} else if (cellData instanceof Short) {
|
||||
cell.setCellValue((Short) cellData);
|
||||
} else if (cellData instanceof Integer) {
|
||||
cell.setCellValue((Integer) cellData);
|
||||
} else if (cellData instanceof Long) {
|
||||
cell.setCellValue((Long) cellData);
|
||||
} else if (cellData instanceof Float) {
|
||||
cell.setCellValue((Float) cellData);
|
||||
} else {
|
||||
cell.setCellValue(cellModel.getText());
|
||||
}
|
||||
cellStyle.ifPresent(cs -> cell.setCellStyle(cs));
|
||||
return cell;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ ExportCSV.saveNodesToCSV.empty=No data to export
|
||||
# {0} - Output file
|
||||
ExportCSV.saveNodesToCSV.fileExists=File {0} already exists
|
||||
ExportCSV.saveNodesToCSV.noCurrentCase=No open case available
|
||||
ExportCSV.title.text=Export selected rows to CSV
|
||||
ExportCSV.title.text=Export Selected Rows to CSV
|
||||
ExternalViewerAction.actionPerformed.failure.exe.message=The file is an executable and will not be opened.
|
||||
ExternalViewerAction.actionPerformed.failure.IO.message=There is no associated editor for files of this type or the associated application failed to launch.
|
||||
ExternalViewerAction.actionPerformed.failure.missingFile.message=The file no longer exists.
|
||||
|
@ -26,6 +26,7 @@ import java.beans.PropertyVetoException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -35,6 +36,8 @@ import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.prefs.PreferenceChangeEvent;
|
||||
import java.util.prefs.PreferenceChangeListener;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
@ -208,6 +211,41 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
if (!Objects.isNull(views)) {
|
||||
tree.expandNode(views);
|
||||
}
|
||||
|
||||
// expand all nodes parents of and including hosts if group by host/person
|
||||
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
|
||||
Node[] rootNodes = rootChildren.getNodes();
|
||||
if (rootNodes != null) {
|
||||
Stream.of(rootNodes)
|
||||
.flatMap((n) -> getHostNodesAndParents(n).stream())
|
||||
.filter((n) -> n != null)
|
||||
.forEach((n) -> tree.expandNode(n));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all nodes including provided node that are parents of or are hosts.
|
||||
* @param node The parent or possible host node.
|
||||
* @return The descendant host nodes.
|
||||
*/
|
||||
private List<Node> getHostNodesAndParents(Node node) {
|
||||
if (node == null) {
|
||||
return Collections.emptyList();
|
||||
} else if (node.getLookup().lookup(Person.class) != null
|
||||
|| PersonGroupingNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
|
||||
Children children = node.getChildren();
|
||||
Node[] childNodes = children == null ? null : children.getNodes();
|
||||
if (childNodes != null) {
|
||||
return Stream.of(childNodes)
|
||||
.flatMap((n) -> Stream.concat(Stream.of(n), getHostNodesAndParents(n).stream()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
} else if (node.getLookup().lookup(Host.class) != null) {
|
||||
return Arrays.asList(node);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,7 +85,7 @@ public final class ExportCSVAction extends AbstractAction {
|
||||
/**
|
||||
* Private constructor for the action.
|
||||
*/
|
||||
@NbBundle.Messages({"ExportCSV.title.text=Export selected rows to CSV"})
|
||||
@NbBundle.Messages({"ExportCSV.title.text=Export Selected Rows to CSV"})
|
||||
private ExportCSVAction() {
|
||||
super(Bundle.ExportCSV_title_text());
|
||||
}
|
||||
|
@ -156,12 +156,11 @@ public class DiscoveryAttributes {
|
||||
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
|
||||
CentralRepository centralRepoDb) throws DiscoveryException {
|
||||
try {
|
||||
Map<String, String> domainsToCategories = getDomainsWithWebCategories(caseDb);
|
||||
Map<String, Set<String>> domainsToCategories = getDomainsWithWebCategories(caseDb);
|
||||
for (Result result : results) {
|
||||
if (result instanceof ResultDomain) {
|
||||
ResultDomain domain = (ResultDomain) result;
|
||||
String webCategory = domainsToCategories.get(domain.getDomain());
|
||||
domain.setWebCategory(webCategory);
|
||||
domain.addWebCategories(domainsToCategories.get(domain.getDomain()));
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException | InterruptedException ex) {
|
||||
@ -174,24 +173,23 @@ public class DiscoveryAttributes {
|
||||
* the category name attribute. Each ResultDomain is then parsed and
|
||||
* matched against this map of values.
|
||||
*/
|
||||
private Map<String, String> getDomainsWithWebCategories(SleuthkitCase caseDb) throws TskCoreException, InterruptedException {
|
||||
Map<String, String> domainToCategory = new HashMap<>();
|
||||
private Map<String, Set<String>> getDomainsWithWebCategories(SleuthkitCase caseDb) throws TskCoreException, InterruptedException {
|
||||
Map<String, Set<String>> domainToCategory = new HashMap<>();
|
||||
|
||||
for (BlackboardArtifact artifact : caseDb.getBlackboardArtifacts(TSK_WEB_CATEGORIZATION)) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
|
||||
BlackboardAttribute webCategory = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME));
|
||||
BlackboardAttribute domain = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN));
|
||||
|
||||
if (webCategory != null && domain != null) {
|
||||
String webCatDisplayName = webCategory.getValueString();
|
||||
String domainDisplayName = domain.getValueString().trim().toLowerCase();
|
||||
domainToCategory.put(domainDisplayName, webCatDisplayName);
|
||||
if (!domainToCategory.containsKey(domainDisplayName)) {
|
||||
domainToCategory.put(domainDisplayName, new HashSet<>());
|
||||
}
|
||||
domainToCategory.get(domainDisplayName).add(webCategory.getValueString());
|
||||
}
|
||||
}
|
||||
|
||||
return domainToCategory;
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
@ -802,12 +804,14 @@ public class DiscoveryKeyUtils {
|
||||
*/
|
||||
static class DomainCategoryGroupKey extends GroupKey {
|
||||
|
||||
private final String webCategory;
|
||||
private final Set<String> webCategories = new HashSet<>();
|
||||
private final String displayName;
|
||||
|
||||
DomainCategoryGroupKey(Result result) {
|
||||
if (result instanceof ResultDomain) {
|
||||
ResultDomain domain = (ResultDomain) result;
|
||||
this.webCategory = domain.getWebCategory();
|
||||
this.webCategories.addAll(domain.getWebCategories());
|
||||
displayName = String.join(",", webCategories);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Input result should be of type ResultDomain");
|
||||
}
|
||||
@ -815,7 +819,8 @@ public class DiscoveryKeyUtils {
|
||||
|
||||
@Override
|
||||
String getDisplayName() {
|
||||
return this.webCategory;
|
||||
|
||||
return this.displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -828,21 +833,27 @@ public class DiscoveryKeyUtils {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getWebCategory());
|
||||
return Objects.hash(webCategories);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(GroupKey otherGroupKey) {
|
||||
if (otherGroupKey instanceof DomainCategoryGroupKey) {
|
||||
DomainCategoryGroupKey webCategoryKey = (DomainCategoryGroupKey) otherGroupKey;
|
||||
return this.webCategory.compareTo(webCategoryKey.getWebCategory());
|
||||
if (webCategories.size() != ((DomainCategoryGroupKey) otherGroupKey).getWebCategories().size()) {
|
||||
return 1;
|
||||
}
|
||||
if (webCategories.containsAll(((DomainCategoryGroupKey) otherGroupKey).getWebCategories())) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return compareClassNames(otherGroupKey);
|
||||
}
|
||||
}
|
||||
|
||||
String getWebCategory() {
|
||||
return this.webCategory;
|
||||
Set<String> getWebCategories() {
|
||||
return Collections.unmodifiableSet(webCategories);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1198,9 +1209,9 @@ public class DiscoveryKeyUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next closed Sunday given an epoch time and timezone.
|
||||
* Dates for grouping are managed on a weekly basis. Each Sunday
|
||||
* acts as the boundary and representative for the week.
|
||||
* Get the next closed Sunday given an epoch time and timezone. Dates for
|
||||
* grouping are managed on a weekly basis. Each Sunday acts as the boundary
|
||||
* and representative for the week.
|
||||
*/
|
||||
private static ZonedDateTime getCurrentWeekCutOff(long epochSeconds, ResultDomain domainResult) {
|
||||
Instant startActivityAsInsant = Instant.ofEpochSecond(epochSeconds);
|
||||
@ -1274,9 +1285,8 @@ public class DiscoveryKeyUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Key representing the number of page views.
|
||||
* Page views are defined as the number of TSK_WEB_HISTORY artifacts that match
|
||||
* a domain value.
|
||||
* Key representing the number of page views. Page views are defined as the
|
||||
* number of TSK_WEB_HISTORY artifacts that match a domain value.
|
||||
*/
|
||||
static class PageViewsGroupKey extends GroupKey {
|
||||
|
||||
|
@ -95,7 +95,6 @@ public class DomainSearch {
|
||||
final Map<GroupKey, List<Result>> searchResults = searchCache.get(
|
||||
userName, filters, groupAttributeType, groupSortingType,
|
||||
domainSortingMethod, caseDb, centralRepoDb);
|
||||
|
||||
// Transform the cached results into a map of group key to group size.
|
||||
final LinkedHashMap<GroupKey, Integer> groupSizes = new LinkedHashMap<>();
|
||||
for (GroupKey groupKey : searchResults.keySet()) {
|
||||
@ -139,7 +138,6 @@ public class DomainSearch {
|
||||
userName, filters, groupAttributeType, groupSortingType,
|
||||
domainSortingMethod, caseDb, centralRepoDb);
|
||||
final List<Result> domainsInGroup = searchResults.get(groupKey);
|
||||
|
||||
final List<Result> page = new ArrayList<>();
|
||||
for (int i = startingEntry; (i < startingEntry + numberOfEntries)
|
||||
&& (i < domainsInGroup.size()); i++) {
|
||||
|
@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.discovery.search;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
@ -36,7 +38,7 @@ public class ResultDomain extends Result {
|
||||
private final Long filesDownloaded;
|
||||
private final Long countOfKnownAccountTypes;
|
||||
private final String accountTypes;
|
||||
private String webCategory;
|
||||
private final Set<String> webCategories = new HashSet<>();
|
||||
|
||||
private final Content dataSource;
|
||||
private final long dataSourceId;
|
||||
@ -60,6 +62,24 @@ public class ResultDomain extends Result {
|
||||
this.accountTypes = accountTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a copy of the specified ResultDomain, without a category set.
|
||||
*
|
||||
* @param resultDomain The ResultDomain to copy
|
||||
*/
|
||||
ResultDomain(ResultDomain resultDomain) {
|
||||
this.domain = resultDomain.getDomain();
|
||||
this.dataSource = resultDomain.getDataSource();
|
||||
this.dataSourceId = resultDomain.getDataSourceObjectId();
|
||||
this.activityStart = resultDomain.getActivityStart();
|
||||
this.activityEnd = resultDomain.getActivityEnd();
|
||||
this.totalPageViews = resultDomain.getTotalPageViews();
|
||||
this.pageViewsInLast60 = resultDomain.getPageViewsInLast60Days();
|
||||
this.filesDownloaded = resultDomain.getFilesDownloaded();
|
||||
this.countOfKnownAccountTypes = resultDomain.getCountOfKnownAccountTypes();
|
||||
this.accountTypes = resultDomain.getAccountTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the domain represented as a String.
|
||||
*
|
||||
@ -123,20 +143,24 @@ public class ResultDomain extends Result {
|
||||
@NbBundle.Messages({
|
||||
"ResultDomain_getDefaultCategory=Uncategorized"
|
||||
})
|
||||
public String getWebCategory() {
|
||||
if (webCategory == null) {
|
||||
return Bundle.ResultDomain_getDefaultCategory();
|
||||
public Set<String> getWebCategories() {
|
||||
Set<String> returnList = new HashSet<>();
|
||||
if (webCategories.isEmpty()) {
|
||||
returnList.add(Bundle.ResultDomain_getDefaultCategory());
|
||||
} else {
|
||||
return webCategory;
|
||||
returnList.addAll(webCategories);
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the web category for this domain (derived from TSK_WEB_CATEGORY)
|
||||
* Add the web categories for this domain (derived from TSK_WEB_CATEGORY)
|
||||
* artifacts.
|
||||
*/
|
||||
public void setWebCategory(String webCategory) {
|
||||
this.webCategory = webCategory;
|
||||
public void addWebCategories(Set<String> categories) {
|
||||
if (categories != null && !categories.isEmpty()) {
|
||||
this.webCategories.addAll(categories);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -144,8 +168,8 @@ public class ResultDomain extends Result {
|
||||
* (TSK_WEB_ACCOUNT_TYPE).
|
||||
*/
|
||||
public boolean hasKnownAccountType() {
|
||||
return countOfKnownAccountTypes != null
|
||||
&& countOfKnownAccountTypes > 0;
|
||||
return getCountOfKnownAccountTypes() != null
|
||||
&& getCountOfKnownAccountTypes() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -192,4 +216,11 @@ public class ResultDomain extends Result {
|
||||
+ this.pageViewsInLast60 + ", downloads=" + this.filesDownloaded + ", frequency="
|
||||
+ this.getFrequency() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the countOfKnownAccountTypes
|
||||
*/
|
||||
Long getCountOfKnownAccountTypes() {
|
||||
return countOfKnownAccountTypes;
|
||||
}
|
||||
}
|
||||
|
@ -768,7 +768,6 @@ public class SearchFiltering {
|
||||
CentralRepository centralRepoDb) throws DiscoveryException {
|
||||
DiscoveryAttributes.PreviouslyNotableAttribute previouslyNotableAttr = new DiscoveryAttributes.PreviouslyNotableAttribute();
|
||||
previouslyNotableAttr.addAttributeToResults(currentResults, caseDb, centralRepoDb);
|
||||
|
||||
List<Result> filteredResults = new ArrayList<>();
|
||||
for (Result file : currentResults) {
|
||||
if (file.getPreviouslyNotableInCR() == SearchData.PreviouslyNotable.PREVIOUSLY_NOTABLE) {
|
||||
|
@ -21,10 +21,13 @@ package org.sleuthkit.autopsy.discovery.search;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey;
|
||||
|
||||
/**
|
||||
@ -75,14 +78,45 @@ class SearchResults {
|
||||
void add(List<Result> results) {
|
||||
for (Result result : results) {
|
||||
// Add the file to the appropriate group, creating it if necessary
|
||||
if (result.getType() == SearchData.Type.DOMAIN && attrType instanceof DiscoveryAttributes.DomainCategoryAttribute) {
|
||||
/**
|
||||
* This section is to add results to individual groups based on
|
||||
* the individual Web Categories the domain is part of instead
|
||||
* of the combination of categories. So that results will show
|
||||
* up in every group for which they have a category.
|
||||
*/
|
||||
for (String category : ((ResultDomain) result).getWebCategories()) {
|
||||
if (!StringUtils.isBlank(category)) {
|
||||
ResultDomain currentResult = (ResultDomain) result;
|
||||
Set<String> newCategorySet = new HashSet<>();
|
||||
newCategorySet.add(category);
|
||||
ResultDomain copyOfResult = new ResultDomain(currentResult);
|
||||
copyOfResult.addWebCategories(newCategorySet);
|
||||
GroupKey groupKey = attrType.getGroupKey(copyOfResult);
|
||||
//purposefully adding original instead of copy so it will display all categories when looking at domain
|
||||
addResultToGroupMap(groupKey, result);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GroupKey groupKey = attrType.getGroupKey(result);
|
||||
addResultToGroupMap(groupKey, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Private helper method to add a result to the groupMap with a specified
|
||||
* key.
|
||||
*
|
||||
* @param groupKey The key to add the result under.
|
||||
* @param result The result to add.
|
||||
*/
|
||||
private void addResultToGroupMap(GroupKey groupKey, Result result) {
|
||||
if (!groupMap.containsKey(groupKey)) {
|
||||
groupMap.put(groupKey, new Group(groupSortingType, groupKey));
|
||||
}
|
||||
groupMap.get(groupKey).addResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run after all files have been added to sortGroupsAndFiles the groups and
|
||||
@ -166,7 +200,6 @@ class SearchResults {
|
||||
for (Group group : groupList) {
|
||||
map.put(group.getGroupKey(), group.getResults());
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ DomainDetailsPanel.otherOccurrencesTab.title=Other Occurrences
|
||||
# {0} - startDate
|
||||
# {1} - endDate
|
||||
DomainSummaryPanel.activity.text=Activity: {0} to {1}
|
||||
DomainSummaryPanel.category.text=Category:
|
||||
DomainSummaryPanel.category.text=Categories:
|
||||
DomainSummaryPanel.downloads.text=Files downloaded:
|
||||
DomainSummaryPanel.loadingImages.text=Loading thumbnail...
|
||||
DomainSummaryPanel.no.text=No
|
||||
|
@ -24,7 +24,9 @@ import java.awt.Point;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.TimeZone;
|
||||
import java.util.Set;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JList;
|
||||
@ -171,7 +173,7 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
|
||||
"DomainSummaryPanel.downloads.text=Files downloaded: ",
|
||||
"DomainSummaryPanel.notability.text=Previously tagged as notable: ",
|
||||
"DomainSummaryPanel.userRole.text=Account type: ",
|
||||
"DomainSummaryPanel.category.text=Category: ",
|
||||
"DomainSummaryPanel.category.text=Categories: ",
|
||||
"DomainSummaryPanel.loadingImages.text=Loading thumbnail...",
|
||||
"DomainSummaryPanel.no.text=No",
|
||||
"DomainSummaryPanel.yes.text=Yes"})
|
||||
@ -188,7 +190,9 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
|
||||
notability += Bundle.DomainSummaryPanel_no_text();
|
||||
}
|
||||
domainNotabilityLabel.setText(notability);
|
||||
categoryLabel.setText(Bundle.DomainSummaryPanel_category_text() + value.getResultDomain().getWebCategory());
|
||||
Set<String> webCategories = new HashSet<>();
|
||||
webCategories.addAll(value.getResultDomain().getWebCategories());
|
||||
categoryLabel.setText(Bundle.DomainSummaryPanel_category_text() + String.join(",", webCategories));
|
||||
activityLabel.setText(Bundle.DomainSummaryPanel_activity_text(startDate, endDate));
|
||||
totalVisitsLabel.setText(Bundle.DomainSummaryPanel_totalPages_text() + value.getResultDomain().getTotalPageViews());
|
||||
pagesLabel.setText(Bundle.DomainSummaryPanel_pages_text() + value.getResultDomain().getPageViewsInLast60Days());
|
||||
|
@ -1,4 +1,5 @@
|
||||
CTL_RunIngestAction=Run Ingest
|
||||
FileIngestPipeline_SaveResults_Activity=Saving Results
|
||||
IngestJobSettingsPanel.IngestModulesTableRenderer.info.message=A previous version of this ingest module has been run before on this data source.
|
||||
IngestJobSettingsPanel.IngestModulesTableRenderer.warning.message=This ingest module has been run before on this data source.
|
||||
IngestJobSettingsPanel.noPerRunSettings=The selected module has no per-run settings.
|
||||
|
@ -95,9 +95,9 @@ final class DataSourceIngestPipeline extends IngestTaskPipeline<DataSourceIngest
|
||||
if (!ingestJobPipeline.isCancelled() && ingestJobPipeline.currentDataSourceIngestModuleIsCancelled()) {
|
||||
ingestJobPipeline.currentDataSourceIngestModuleCancellationCompleted(getDisplayName());
|
||||
}
|
||||
if (result == ProcessResult.ERROR) {
|
||||
throw new IngestModuleException(String.format("%s experienced an error analyzing %s (data source objId = %d)", getDisplayName(), dataSource.getName(), dataSource.getId())); //NON-NLS
|
||||
}
|
||||
// if (result == ProcessResult.ERROR) {
|
||||
// throw new IngestModuleException(String.format("%s experienced an error analyzing %s (data source objId = %d)", getDisplayName(), dataSource.getName(), dataSource.getId())); //NON-NLS
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.ingest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
@ -60,7 +61,11 @@ final class FileIngestPipeline extends IngestTaskPipeline<FileIngestTask> {
|
||||
}
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages({
|
||||
"FileIngestPipeline_SaveResults_Activity=Saving Results"
|
||||
})
|
||||
void completeTask(FileIngestTask task) throws IngestTaskPipelineException {
|
||||
ingestManager.setIngestTaskProgress(task, Bundle.FileIngestPipeline_SaveResults_Activity());
|
||||
AbstractFile file = null;
|
||||
try {
|
||||
file = task.getFile();
|
||||
@ -118,9 +123,9 @@ final class FileIngestPipeline extends IngestTaskPipeline<FileIngestTask> {
|
||||
ingestManager.setIngestTaskProgress(task, getDisplayName());
|
||||
ingestJobPipeline.setCurrentFileIngestModule(getDisplayName(), file.getName());
|
||||
ProcessResult result = module.process(file);
|
||||
if (result == ProcessResult.ERROR) {
|
||||
throw new IngestModuleException(String.format("%s experienced an error analyzing %s (file objId = %d)", getDisplayName(), file.getName(), file.getId())); //NON-NLS
|
||||
}
|
||||
// if (result == ProcessResult.ERROR) {
|
||||
// throw new IngestModuleException(String.format("%s experienced an error analyzing %s (file objId = %d)", getDisplayName(), file.getName(), file.getId())); //NON-NLS
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import java.awt.EventQueue;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -46,6 +47,7 @@ import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.netbeans.api.progress.ProgressHandle;
|
||||
import org.openide.util.Cancellable;
|
||||
import org.openide.util.NbBundle;
|
||||
@ -289,7 +291,8 @@ public class IngestManager implements IngestProgressSnapshotProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ingest stream from the given ingest settings for a data source.
|
||||
* Creates an ingest stream from the given ingest settings for a data
|
||||
* source.
|
||||
*
|
||||
* @param dataSource The data source
|
||||
* @param settings The ingest job settings.
|
||||
@ -313,7 +316,6 @@ public class IngestManager implements IngestProgressSnapshotProvider {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the number of file ingest threads the ingest manager is using to do
|
||||
* ingest jobs.
|
||||
@ -395,6 +397,21 @@ public class IngestManager implements IngestProgressSnapshotProvider {
|
||||
"IngestManager.startupErr.dlgErrorList=Errors:"
|
||||
})
|
||||
IngestJobStartResult startIngestJob(IngestJob job) {
|
||||
|
||||
// initialize IngestMessageInbox, if it hasn't been initialized yet. This can't be done in
|
||||
// the constructor because that ends up freezing the UI on startup (JIRA-7345).
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
initIngestMessageInbox();
|
||||
} else {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(() -> initIngestMessageInbox());
|
||||
} catch (InterruptedException ex) {
|
||||
// ignore interruptions
|
||||
} catch (InvocationTargetException ex) {
|
||||
logger.log(Level.WARNING, "There was an error starting ingest message inbox", ex);
|
||||
}
|
||||
}
|
||||
|
||||
List<IngestModuleError> errors = null;
|
||||
Case openCase;
|
||||
try {
|
||||
@ -521,7 +538,8 @@ public class IngestManager implements IngestProgressSnapshotProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an ingest job event property change listener for the given event types.
|
||||
* Adds an ingest job event property change listener for the given event
|
||||
* types.
|
||||
*
|
||||
* @param eventTypes The event types to listen for
|
||||
* @param listener The PropertyChangeListener to be added
|
||||
@ -563,7 +581,8 @@ public class IngestManager implements IngestProgressSnapshotProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an ingest module event property change listener for given event types.
|
||||
* Adds an ingest module event property change listener for given event
|
||||
* types.
|
||||
*
|
||||
* @param eventTypes The event types to listen for
|
||||
* @param listener The PropertyChangeListener to be removed.
|
||||
@ -699,8 +718,11 @@ public class IngestManager implements IngestProgressSnapshotProvider {
|
||||
|
||||
/**
|
||||
* Causes the ingest manager to get the top component used to display ingest
|
||||
* inbox messages. Called by the custom installer for this package once the
|
||||
* window system is initialized.
|
||||
* inbox messages. Used to be called by the custom installer for this
|
||||
* package once the window system is initialized, but that results in a lot
|
||||
* of UI components being initialized, which freezes the UI for a long
|
||||
* period of time(JIRA-7345). Instead we are now initializing
|
||||
* IngestMessageInbox immediately prior to running first ingest job.
|
||||
*/
|
||||
void initIngestMessageInbox() {
|
||||
synchronized (this.ingestMessageBoxLock) {
|
||||
@ -747,67 +769,65 @@ public class IngestManager implements IngestProgressSnapshotProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ingest job snapshot when a data source level ingest job task
|
||||
* starts to be processd by a data source ingest module in the data source
|
||||
* ingest modules pipeline of an ingest job.
|
||||
* Updates the ingest progress snapshot when a new ingest module starts
|
||||
* working on a data source level ingest task.
|
||||
*
|
||||
* @param task The data source level ingest job task that
|
||||
* was started.
|
||||
* @param ingestModuleDisplayName The dislpay name of the data source level
|
||||
* ingest module that has started processing
|
||||
* the task.
|
||||
* @param task The data source ingest task.
|
||||
* @param currentModuleName The display name of the currently processing
|
||||
* module.
|
||||
*/
|
||||
void setIngestTaskProgress(DataSourceIngestTask task, String ingestModuleDisplayName) {
|
||||
ingestThreadActivitySnapshots.put(task.getThreadId(), new IngestThreadActivitySnapshot(task.getThreadId(), task.getIngestJobPipeline().getId(), ingestModuleDisplayName, task.getDataSource()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ingest job snapshot when a file source level ingest job task
|
||||
* starts to be processed by a file level ingest module in the file ingest
|
||||
* modules pipeline of an ingest job.
|
||||
*
|
||||
* @param task The file level ingest job task that was
|
||||
* started.
|
||||
* @param ingestModuleDisplayName The dislpay name of the file level ingest
|
||||
* module that has started processing the
|
||||
* task.
|
||||
*/
|
||||
void setIngestTaskProgress(FileIngestTask task, String ingestModuleDisplayName) {
|
||||
void setIngestTaskProgress(DataSourceIngestTask task, String currentModuleName) {
|
||||
IngestThreadActivitySnapshot prevSnap = ingestThreadActivitySnapshots.get(task.getThreadId());
|
||||
IngestThreadActivitySnapshot newSnap;
|
||||
try {
|
||||
newSnap = new IngestThreadActivitySnapshot(task.getThreadId(), task.getIngestJobPipeline().getId(), ingestModuleDisplayName, task.getDataSource(), task.getFile());
|
||||
} catch (TskCoreException ex) {
|
||||
// In practice, this task would never have been enqueued or processed since the file
|
||||
// lookup would have failed.
|
||||
newSnap = new IngestThreadActivitySnapshot(task.getThreadId(), task.getIngestJobPipeline().getId(), ingestModuleDisplayName, task.getDataSource());
|
||||
}
|
||||
IngestThreadActivitySnapshot newSnap = new IngestThreadActivitySnapshot(task.getThreadId(), task.getIngestJobPipeline().getId(), currentModuleName, task.getDataSource());
|
||||
ingestThreadActivitySnapshots.put(task.getThreadId(), newSnap);
|
||||
|
||||
/*
|
||||
* Update the total run time for the PREVIOUS ingest module in the
|
||||
* pipeline, which has now finished its processing for the task.
|
||||
*/
|
||||
incrementModuleRunTime(prevSnap.getActivity(), newSnap.getStartTime().getTime() - prevSnap.getStartTime().getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ingest job snapshot when a data source level ingest job task
|
||||
* is completed by the data source ingest modules in the data source ingest
|
||||
* modules pipeline of an ingest job.
|
||||
* Updates the ingest progress snapshot when a new ingest module starts
|
||||
* working on a file ingest task.
|
||||
*
|
||||
* @param task The data source level ingest job task that was completed.
|
||||
* @param task The file ingest task.
|
||||
* @param currentModuleName The display name of the currently processing
|
||||
* module.
|
||||
*/
|
||||
void setIngestTaskProgressCompleted(DataSourceIngestTask task) {
|
||||
ingestThreadActivitySnapshots.put(task.getThreadId(), new IngestThreadActivitySnapshot(task.getThreadId()));
|
||||
void setIngestTaskProgress(FileIngestTask task, String currentModuleName) {
|
||||
IngestThreadActivitySnapshot prevSnap = ingestThreadActivitySnapshots.get(task.getThreadId());
|
||||
IngestThreadActivitySnapshot newSnap;
|
||||
try {
|
||||
newSnap = new IngestThreadActivitySnapshot(task.getThreadId(), task.getIngestJobPipeline().getId(), currentModuleName, task.getDataSource(), task.getFile());
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting file from file ingest task", ex);
|
||||
newSnap = new IngestThreadActivitySnapshot(task.getThreadId(), task.getIngestJobPipeline().getId(), currentModuleName, task.getDataSource());
|
||||
}
|
||||
ingestThreadActivitySnapshots.put(task.getThreadId(), newSnap);
|
||||
|
||||
/*
|
||||
* Update the total run time for the PREVIOUS ingest module in the
|
||||
* pipeline, which has now finished its processing for the task.
|
||||
*/
|
||||
incrementModuleRunTime(prevSnap.getActivity(), newSnap.getStartTime().getTime() - prevSnap.getStartTime().getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ingest job snapshot when a file level ingest job task is
|
||||
* completed by the file ingest modules in the file ingest modules pipeline
|
||||
* of an ingest job.
|
||||
* Updates the ingest progress snapshot when an ingest task is completed.
|
||||
*
|
||||
* @param task The file level ingest job task that was completed.
|
||||
* @param task The ingest task.
|
||||
*/
|
||||
void setIngestTaskProgressCompleted(FileIngestTask task) {
|
||||
void setIngestTaskProgressCompleted(IngestTask task) {
|
||||
IngestThreadActivitySnapshot prevSnap = ingestThreadActivitySnapshots.get(task.getThreadId());
|
||||
IngestThreadActivitySnapshot newSnap = new IngestThreadActivitySnapshot(task.getThreadId());
|
||||
ingestThreadActivitySnapshots.put(task.getThreadId(), newSnap);
|
||||
|
||||
/*
|
||||
* Update the total run time for the LAST ingest module in the pipeline,
|
||||
* which has now finished its processing for the task.
|
||||
*/
|
||||
incrementModuleRunTime(prevSnap.getActivity(), newSnap.getStartTime().getTime() - prevSnap.getStartTime().getTime());
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2014 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");
|
||||
@ -19,7 +19,6 @@
|
||||
package org.sleuthkit.autopsy.ingest;
|
||||
|
||||
import org.openide.modules.ModuleInstall;
|
||||
import org.openide.windows.WindowManager;
|
||||
|
||||
/**
|
||||
* Initializes ingest manager when the module is loaded
|
||||
@ -41,15 +40,8 @@ public class Installer extends ModuleInstall {
|
||||
|
||||
@Override
|
||||
public void restored() {
|
||||
final IngestManager manager = IngestManager.getInstance();
|
||||
WindowManager.getDefault().invokeWhenUIReady(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
//at this point UI top component is present for sure, ensure manager has it
|
||||
manager.initIngestMessageInbox();
|
||||
}
|
||||
});
|
||||
|
||||
// initialize ingest manager
|
||||
IngestManager.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -20,10 +20,15 @@ package org.sleuthkit.autopsy.machinesettings;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Optional;
|
||||
import java.util.prefs.Preferences;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbPreferences;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.FileUtil;
|
||||
|
||||
/**
|
||||
@ -33,26 +38,122 @@ public final class UserMachinePreferences {
|
||||
|
||||
private static final Preferences preferences = NbPreferences.forModule(UserMachinePreferences.class);
|
||||
|
||||
private static final String TEMP_DIR_KEY = "TempDirectory";
|
||||
/**
|
||||
* The user specified choice for where the temp directory should be located.
|
||||
*/
|
||||
public enum TempDirChoice {
|
||||
/**
|
||||
* A custom location specified with an absolute path by the user.
|
||||
*/
|
||||
CUSTOM,
|
||||
/**
|
||||
* Equivalent to java.io.tmpdir.
|
||||
*/
|
||||
SYSTEM,
|
||||
/**
|
||||
* If a case is open, a sub directory of the case.
|
||||
*/
|
||||
CASE;
|
||||
|
||||
/**
|
||||
* Retrieves a default temporary directory that is a subdirectory of
|
||||
* java.io.tmpdir.
|
||||
* Returns the temp directory choice that matches the string provided
|
||||
* (whitespace and case insensitive).
|
||||
*
|
||||
* @return The absolute path to the temp directory.
|
||||
* @param val The string value.
|
||||
* @return The choice or empty if not found.
|
||||
*/
|
||||
private static String getDefaultTempDirectory() {
|
||||
return Paths.get(System.getProperty("java.io.tmpdir")).toAbsolutePath().toString();
|
||||
static Optional<TempDirChoice> getValue(String val) {
|
||||
if (val == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Stream.of(TempDirChoice.values())
|
||||
.filter(tempChoice -> tempChoice.name().equalsIgnoreCase(val.trim()))
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
|
||||
private static final String CUSTOM_TEMP_DIR_KEY = "TempDirectory";
|
||||
private static final String TEMP_DIR_CHOICE_KEY = "TempDirChoice";
|
||||
|
||||
private static final String AUTOPSY_SUBDIR = UserPreferences.getAppName();
|
||||
private static final String CASE_SUBDIR = "Temp";
|
||||
|
||||
private static final TempDirChoice DEFAULT_CHOICE = TempDirChoice.SYSTEM;
|
||||
|
||||
/**
|
||||
* @return A subdirectory of java.io.tmpdir.
|
||||
*/
|
||||
private static File getSystemTempDirFile() {
|
||||
return Paths.get(System.getProperty("java.io.tmpdir"), AUTOPSY_SUBDIR).toFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the base user-specified temporary directory.
|
||||
* @return A subdirectory of the open case or getSystemTempDirFile if no
|
||||
* open case.
|
||||
*/
|
||||
private static File getCaseTempDirFile() {
|
||||
try {
|
||||
String caseDirStr = Case.getCurrentCaseThrows().getCaseDirectory();
|
||||
return Paths.get(caseDirStr, CASE_SUBDIR).toFile();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
return getSystemTempDirFile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the custom directory subdirectory to be used for temp files
|
||||
* (otherwise java.io.tmpdir subdir).
|
||||
*
|
||||
* @return A subdirectory of the custom user-specified path. If no path is
|
||||
* specified, getSystemTempDirFile() is returned instead.
|
||||
*/
|
||||
private static File getCustomTempDirFile() {
|
||||
String customDirectory = getCustomTempDirectory();
|
||||
return (StringUtils.isBlank(customDirectory))
|
||||
? getSystemTempDirFile() : Paths.get(customDirectory, AUTOPSY_SUBDIR).toFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the temp directory file to use based on user choice.
|
||||
*
|
||||
* @return The directory.
|
||||
*/
|
||||
private static File getTempDirFile() {
|
||||
TempDirChoice choice = getTempDirChoice();
|
||||
switch (choice) {
|
||||
case CASE:
|
||||
return getCaseTempDirFile();
|
||||
case CUSTOM:
|
||||
return getCustomTempDirFile();
|
||||
case SYSTEM:
|
||||
default:
|
||||
return getSystemTempDirFile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the temp directory to use based on settings. This method also
|
||||
* ensures the temp directory has been created.
|
||||
*
|
||||
* @return The base user-specified temporary directory.
|
||||
*/
|
||||
public static String getBaseTempDirectory() {
|
||||
String tempDir = preferences.get(TEMP_DIR_KEY, null);
|
||||
return StringUtils.isBlank(tempDir) ? getDefaultTempDirectory() : tempDir;
|
||||
public static String getTempDirectory() {
|
||||
File dir = getTempDirFile();
|
||||
dir = dir == null ? getSystemTempDirFile() : dir;
|
||||
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
return dir.getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The user-specified custom temp directory path or empty string.
|
||||
*/
|
||||
public static String getCustomTempDirectory() {
|
||||
return preferences.get(CUSTOM_TEMP_DIR_KEY, "");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,8 +165,7 @@ public final class UserMachinePreferences {
|
||||
* @return True if this is a valid location for a temp directory.
|
||||
*
|
||||
* @throws UserMachinePreferencesException If path could not be validated
|
||||
* due to mkdirs failure or the
|
||||
* directory is not read/write.
|
||||
* due to mkdirs failure or the directory is not read/write.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"# {0} - path",
|
||||
@ -98,9 +198,31 @@ public final class UserMachinePreferences {
|
||||
* @throws UserMachinePreferencesException If the directory cannot be
|
||||
* accessed or created.
|
||||
*/
|
||||
public static void setBaseTempDirectory(String path) throws UserMachinePreferencesException {
|
||||
public static void setCustomTempDirectory(String path) throws UserMachinePreferencesException {
|
||||
validateTempDirectory(path);
|
||||
preferences.put(TEMP_DIR_KEY, path);
|
||||
preferences.put(CUSTOM_TEMP_DIR_KEY, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The user selection for how the temp directory should be handled
|
||||
* (temp directory in case folder, in java.io.tmpdir, custom path).
|
||||
*/
|
||||
public static TempDirChoice getTempDirChoice() {
|
||||
return TempDirChoice.getValue(preferences.get(TEMP_DIR_CHOICE_KEY, null))
|
||||
.orElse(DEFAULT_CHOICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the temp directory choice (i.e. system, case, custom).
|
||||
* @param tempDirChoice The choice (must be non-null).
|
||||
* @throws UserMachinePreferencesException
|
||||
*/
|
||||
public static void setTempDirChoice(TempDirChoice tempDirChoice) throws UserMachinePreferencesException {
|
||||
if (tempDirChoice == null) {
|
||||
throw new UserMachinePreferencesException("Expected non-null temp dir choice");
|
||||
}
|
||||
|
||||
preferences.put(TEMP_DIR_CHOICE_KEY, tempDirChoice.name());
|
||||
}
|
||||
|
||||
private UserMachinePreferences() {
|
||||
|
@ -2,7 +2,7 @@
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2014 Basis Technology Corp.
|
||||
* Copyright 2013-2021 Basis Technology Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.report;
|
||||
import org.sleuthkit.autopsy.report.infrastructure.ReportGenerator;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.openide.util.NbBundle;
|
||||
@ -37,7 +39,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
public final class ReportBranding implements ReportBrandingProviderI {
|
||||
|
||||
//property names
|
||||
public static final String AGENCY_LOGO_PATH_PROP = "AgencyLogoPath"; //NON-NLS
|
||||
private static final String AGENCY_LOGO_PATH_PROP = "AgencyLogoPath"; //NON-NLS
|
||||
private static final String REPORT_TITLE_PROP = "ReportTitle"; //NON-NLS
|
||||
private static final String REPORT_FOOTER_PROP = "ReportFooter"; //NON-NLS
|
||||
//default settings
|
||||
@ -46,8 +48,9 @@ public final class ReportBranding implements ReportBrandingProviderI {
|
||||
.getMessage(ReportBranding.class, "ReportBranding.defaultReportTitle.text");
|
||||
private static final String DEFAULT_REPORT_FOOTER = NbBundle
|
||||
.getMessage(ReportBranding.class, "ReportBranding.defaultReportFooter.text");
|
||||
private String reportsBrandingDir; //dir with extracted reports branding resources
|
||||
public static final String MODULE_NAME = ReportBranding.class.getSimpleName();
|
||||
private final String reportsBrandingDir; //dir with extracted reports branding resources
|
||||
private final Path userConfigDir = Paths.get(PlatformUtil.getUserDirectory().getAbsolutePath());
|
||||
private static final String MODULE_NAME = ReportBranding.class.getSimpleName();
|
||||
private static final Logger logger = Logger.getLogger(ReportBranding.class.getName());
|
||||
|
||||
// this is static so that it can be set by another object
|
||||
@ -109,38 +112,73 @@ public final class ReportBranding implements ReportBrandingProviderI {
|
||||
generatorLogoPath = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read logo path from preferences file. Reverses the path relativization performed
|
||||
* in setAgencyLogoPath(). 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 logo file.
|
||||
*/
|
||||
@Override
|
||||
public String getAgencyLogoPath() {
|
||||
String curPath = null;
|
||||
|
||||
/*
|
||||
* The agency logo code uses these properties to persist changes in the
|
||||
* logo (within the same process). This is different from the generator
|
||||
* logo that uses a static variable.
|
||||
*/
|
||||
curPath = ModuleSettings.getConfigSetting(MODULE_NAME, AGENCY_LOGO_PATH_PROP);
|
||||
String curPath = ModuleSettings.getConfigSetting(MODULE_NAME, AGENCY_LOGO_PATH_PROP);
|
||||
|
||||
|
||||
//if has been set, validate it's correct, if not set, return null
|
||||
if (curPath != null && new File(curPath).canRead() == false) {
|
||||
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 setAgencyLogoPath()
|
||||
Path absolutePath = userConfigDir.resolve(curPath);
|
||||
curPath = absolutePath.toString();
|
||||
if (new File(curPath).canRead() == false) {
|
||||
//use default
|
||||
logger.log(Level.INFO, "Custom report branding for agency logo is not valid: " + curPath); //NON-NLS
|
||||
logger.log(Level.INFO, "Custom report branding for agency logo is not valid: {0}", curPath); //NON-NLS
|
||||
curPath = null;
|
||||
}
|
||||
}
|
||||
|
||||
return curPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save logo 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 logo file.
|
||||
*/
|
||||
@Override
|
||||
public void setAgencyLogoPath(String path) {
|
||||
public void setAgencyLogoPath(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.
|
||||
// Should use static variable instead
|
||||
ModuleSettings.setConfigSetting(MODULE_NAME, AGENCY_LOGO_PATH_PROP, path);
|
||||
ModuleSettings.setConfigSetting(MODULE_NAME, AGENCY_LOGO_PATH_PROP, relativePath.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReportTitle() {
|
||||
String curTitle = null;
|
||||
|
||||
curTitle = ModuleSettings.getConfigSetting(MODULE_NAME, REPORT_TITLE_PROP);
|
||||
String curTitle = ModuleSettings.getConfigSetting(MODULE_NAME, REPORT_TITLE_PROP);
|
||||
if (curTitle == null || curTitle.isEmpty()) {
|
||||
//use default
|
||||
logger.log(Level.INFO, "Using default report branding for report title"); //NON-NLS
|
||||
@ -158,9 +196,8 @@ public final class ReportBranding implements ReportBrandingProviderI {
|
||||
|
||||
@Override
|
||||
public String getReportFooter() {
|
||||
String curFooter = null;
|
||||
|
||||
curFooter = ModuleSettings.getConfigSetting(MODULE_NAME, REPORT_FOOTER_PROP);
|
||||
String curFooter = ModuleSettings.getConfigSetting(MODULE_NAME, REPORT_FOOTER_PROP);
|
||||
if (curFooter == null) {
|
||||
//use default
|
||||
logger.log(Level.INFO, "Using default report branding for report footer"); //NON-NLS
|
||||
|
@ -49,19 +49,19 @@ public class TimeLineModule {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get instance of the controller for the current case
|
||||
* Get instance of the controller for the current case.
|
||||
* The controller instance is initialized from a case open event.
|
||||
*
|
||||
* @return the controller for the current case.
|
||||
*
|
||||
* @throws NoCurrentCaseException If there is no case open.
|
||||
* @throws TskCoreException If there was a problem accessing the case
|
||||
* database.
|
||||
*
|
||||
*/
|
||||
public static TimeLineController getController() throws NoCurrentCaseException, TskCoreException {
|
||||
public static TimeLineController getController() throws TskCoreException {
|
||||
synchronized (controllerLock) {
|
||||
if (controller == null) {
|
||||
controller = new TimeLineController(Case.getCurrentCaseThrows());
|
||||
throw new TskCoreException("Timeline controller not initialized");
|
||||
}
|
||||
return controller;
|
||||
}
|
||||
@ -100,13 +100,22 @@ public class TimeLineModule {
|
||||
}
|
||||
controller = null;
|
||||
}
|
||||
} else {
|
||||
// Case is opening - create the controller now
|
||||
synchronized (controllerLock) {
|
||||
try {
|
||||
controller = new TimeLineController(Case.getCurrentCaseThrows());
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Error creating Timeline controller", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
getController().handleCaseEvent(evt);
|
||||
} catch (NoCurrentCaseException ignored) {
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error handling application event", ex);
|
||||
// The call to getController() will only fail due to case closing, so do
|
||||
// not record the error.
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -121,12 +130,9 @@ public class TimeLineModule {
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
try {
|
||||
getController().handleIngestModuleEvent(evt);
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
// ignore
|
||||
return;
|
||||
} catch (TskCoreException ex) {
|
||||
MessageNotifyUtil.Message.error("Error creating timeline controller.");
|
||||
logger.log(Level.SEVERE, "Error creating timeline controller", ex);
|
||||
// The call to getController() will only fail due to case closing, so do
|
||||
// not record the error.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
<!-- for viewers -->
|
||||
<dependency conf="autopsy_core->*" org="org.freedesktop.gstreamer" name="gst1-java-core" rev="1.0.0"/>
|
||||
<dependency conf="autopsy_core->*" org="net.java.dev.jna" name="jna-platform" rev="5.7.0"/>
|
||||
<dependency conf="autopsy_core->*" org="net.java.dev.jna" name="jna-platform" rev="5.8.0"/>
|
||||
|
||||
<!-- for file search -->
|
||||
<dependency conf="autopsy_core->*" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/>
|
||||
|
@ -42,8 +42,8 @@ file.reference.javassist-3.12.1.GA.jar=release/modules/ext/javassist-3.12.1.GA.j
|
||||
file.reference.jfxtras-common-8.0-r4.jar=release/modules/ext/jfxtras-common-8.0-r4.jar
|
||||
file.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-controls-8.0-r4.jar
|
||||
file.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4.jar
|
||||
file.reference.jna-5.7.0.jar=release/modules/ext/jna-5.7.0.jar
|
||||
file.reference.jna-platform-5.7.0.jar=release/modules/ext/jna-platform-5.7.0.jar
|
||||
file.reference.jna-5.8.0.jar=release/modules/ext/jna-5.8.0.jar
|
||||
file.reference.jna-platform-5.8.0.jar=release/modules/ext/jna-platform-5.8.0.jar
|
||||
file.reference.joda-time-2.4.jar=release/modules/ext/joda-time-2.4.jar
|
||||
file.reference.jsr305-1.3.9.jar=release/modules/ext/jsr305-1.3.9.jar
|
||||
file.reference.LGoodDatePicker-10.3.1.jar=release/modules/ext/LGoodDatePicker-10.3.1.jar
|
||||
|
@ -923,8 +923,8 @@
|
||||
<binary-origin>release/modules/ext/commons-compress-1.18.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/jna-platform-5.7.0.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\jna-platform-5.7.0.jar</binary-origin>
|
||||
<runtime-relative-path>ext/jna-platform-5.8.0.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\jna-platform-5.8.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/opencv-248.jar</runtime-relative-path>
|
||||
@ -951,8 +951,8 @@
|
||||
<binary-origin>release/modules/ext/imageio-bmp-3.2.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/jna-5.7.0.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\jna-5.7.0.jar</binary-origin>
|
||||
<runtime-relative-path>ext/jna-5.8.0.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\jna-5.8.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/commons-lang-2.6.jar</runtime-relative-path>
|
||||
|
@ -11,77 +11,28 @@
|
||||
<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,2,-74,0,0,3,-35"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="nodeStatusScrollPane" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="nodeStatusTableTitle" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="refreshButton" linkSize="1" min="-2" pref="100" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="576" max="32767" attributes="0"/>
|
||||
<Component id="healthMonitorButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="clusterMetricsButton" linkSize="1" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace min="-2" pref="40" max="-2" attributes="0"/>
|
||||
<Component id="nodeStatusTableTitle" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="nodeStatusScrollPane" min="-2" pref="215" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="382" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="refreshButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="clusterMetricsButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="healthMonitorButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="refreshButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AinStatusDashboard.refreshButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AinStatusDashboard.refreshButton.toolTipText" 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="refreshButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="clusterMetricsButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AinStatusDashboard.clusterMetricsButton.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="clusterMetricsButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="nodeStatusScrollPane">
|
||||
<Container class="javax.swing.JScrollPane" name="mainScrollPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
<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>
|
||||
<Container class="javax.swing.JPanel" name="mainPanel">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="nodeStatusTableTitle">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
@ -93,18 +44,62 @@
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AinStatusDashboard.nodeStatusTableTitle.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="10" insetsLeft="10" insetsBottom="5" insetsRight="10" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Container class="javax.swing.JPanel" name="buttonPanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="10" insetsLeft="10" insetsBottom="10" insetsRight="10" anchor="10" weightX="1.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="refreshButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AinStatusDashboard.refreshButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AinStatusDashboard.refreshButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[133, 23]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="refreshButtonActionPerformed"/>
|
||||
</Events>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="clusterMetricsButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AinStatusDashboard.clusterMetricsButton.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="clusterMetricsButtonActionPerformed"/>
|
||||
</Events>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="3" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="healthMonitorButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AinStatusDashboard.healthMonitorButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[133, 23]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[133, 23]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[133, 23]"/>
|
||||
</Property>
|
||||
@ -112,6 +107,32 @@
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="healthMonitorButtonActionPerformed"/>
|
||||
</Events>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="2" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.Box$Filler" name="filler1">
|
||||
<Properties>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[32767, 0]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.HorizontalGlue"/>
|
||||
</AuxValues>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 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");
|
||||
@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.experimental.autoingest;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.Insets;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
@ -34,6 +36,8 @@ import org.sleuthkit.autopsy.healthmonitor.HealthMonitorDashboard;
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
final class AinStatusDashboard extends javax.swing.JPanel implements Observer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final AutoIngestMonitor autoIngestMonitor;
|
||||
private final AinStatusPanel nodesPanel;
|
||||
private final static String AIN_REFRESH_THREAD_NAME = "AID-refresh-jobs-%d";
|
||||
@ -50,9 +54,17 @@ final class AinStatusDashboard extends javax.swing.JPanel implements Observer {
|
||||
scheduledRefreshThreadPoolExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat(AIN_REFRESH_THREAD_NAME).build());
|
||||
autoIngestMonitor = monitor;
|
||||
nodesPanel = new AinStatusPanel();
|
||||
nodesPanel.setSize(nodesPanel.getSize());
|
||||
nodeStatusScrollPane.add(nodesPanel);
|
||||
nodeStatusScrollPane.setViewportView(nodesPanel);
|
||||
GridBagConstraints gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 1;
|
||||
gridBagConstraints.fill = GridBagConstraints.BOTH;
|
||||
gridBagConstraints.weightx = 1;
|
||||
gridBagConstraints.weighty = 1;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
|
||||
gridBagConstraints.insets = new Insets(0, 10, 0, 10);
|
||||
mainPanel.add(nodesPanel, gridBagConstraints);
|
||||
|
||||
|
||||
refreshTables();
|
||||
}
|
||||
|
||||
@ -79,20 +91,45 @@ final class AinStatusDashboard extends javax.swing.JPanel implements Observer {
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
java.awt.GridBagConstraints gridBagConstraints;
|
||||
|
||||
javax.swing.JScrollPane mainScrollPane = new javax.swing.JScrollPane();
|
||||
mainPanel = new javax.swing.JPanel();
|
||||
nodeStatusTableTitle = new javax.swing.JLabel();
|
||||
buttonPanel = new javax.swing.JPanel();
|
||||
refreshButton = new javax.swing.JButton();
|
||||
clusterMetricsButton = new javax.swing.JButton();
|
||||
nodeStatusScrollPane = new javax.swing.JScrollPane();
|
||||
nodeStatusTableTitle = new javax.swing.JLabel();
|
||||
healthMonitorButton = new javax.swing.JButton();
|
||||
filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0));
|
||||
|
||||
setLayout(new java.awt.BorderLayout());
|
||||
|
||||
mainPanel.setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
nodeStatusTableTitle.setFont(nodeStatusTableTitle.getFont().deriveFont(nodeStatusTableTitle.getFont().getSize()+3f));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(nodeStatusTableTitle, org.openide.util.NbBundle.getMessage(AinStatusDashboard.class, "AinStatusDashboard.nodeStatusTableTitle.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 0;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.insets = new java.awt.Insets(10, 10, 5, 10);
|
||||
mainPanel.add(nodeStatusTableTitle, gridBagConstraints);
|
||||
|
||||
buttonPanel.setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(refreshButton, org.openide.util.NbBundle.getMessage(AinStatusDashboard.class, "AinStatusDashboard.refreshButton.text")); // NOI18N
|
||||
refreshButton.setToolTipText(org.openide.util.NbBundle.getMessage(AinStatusDashboard.class, "AinStatusDashboard.refreshButton.toolTipText")); // NOI18N
|
||||
refreshButton.setPreferredSize(new java.awt.Dimension(133, 23));
|
||||
refreshButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
refreshButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 0;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
|
||||
buttonPanel.add(refreshButton, gridBagConstraints);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(clusterMetricsButton, org.openide.util.NbBundle.getMessage(AinStatusDashboard.class, "AinStatusDashboard.clusterMetricsButton.text")); // NOI18N
|
||||
clusterMetricsButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
@ -100,56 +137,42 @@ final class AinStatusDashboard extends javax.swing.JPanel implements Observer {
|
||||
clusterMetricsButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
nodeStatusTableTitle.setFont(nodeStatusTableTitle.getFont().deriveFont(nodeStatusTableTitle.getFont().getSize()+3f));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(nodeStatusTableTitle, org.openide.util.NbBundle.getMessage(AinStatusDashboard.class, "AinStatusDashboard.nodeStatusTableTitle.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 3;
|
||||
gridBagConstraints.gridy = 0;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
|
||||
buttonPanel.add(clusterMetricsButton, gridBagConstraints);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(healthMonitorButton, org.openide.util.NbBundle.getMessage(AinStatusDashboard.class, "AinStatusDashboard.healthMonitorButton.text")); // NOI18N
|
||||
healthMonitorButton.setMaximumSize(new java.awt.Dimension(133, 23));
|
||||
healthMonitorButton.setMinimumSize(new java.awt.Dimension(133, 23));
|
||||
healthMonitorButton.setPreferredSize(new java.awt.Dimension(133, 23));
|
||||
healthMonitorButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
healthMonitorButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 2;
|
||||
gridBagConstraints.gridy = 0;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
|
||||
buttonPanel.add(healthMonitorButton, gridBagConstraints);
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 1;
|
||||
gridBagConstraints.gridy = 0;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.weightx = 1.0;
|
||||
buttonPanel.add(filler1, gridBagConstraints);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(nodeStatusScrollPane)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(nodeStatusTableTitle)
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(refreshButton, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 576, Short.MAX_VALUE)
|
||||
.addComponent(healthMonitorButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(clusterMetricsButton)))
|
||||
.addContainerGap())
|
||||
);
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 2;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.weightx = 1.0;
|
||||
gridBagConstraints.insets = new java.awt.Insets(10, 10, 10, 10);
|
||||
mainPanel.add(buttonPanel, gridBagConstraints);
|
||||
|
||||
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {clusterMetricsButton, refreshButton});
|
||||
mainScrollPane.setViewportView(mainPanel);
|
||||
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addGap(40, 40, 40)
|
||||
.addComponent(nodeStatusTableTitle, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(nodeStatusScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(382, 382, 382)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(refreshButton)
|
||||
.addComponent(clusterMetricsButton)
|
||||
.addComponent(healthMonitorButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addContainerGap())
|
||||
);
|
||||
add(mainScrollPane, java.awt.BorderLayout.CENTER);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_refreshButtonActionPerformed
|
||||
@ -172,9 +195,11 @@ final class AinStatusDashboard extends javax.swing.JPanel implements Observer {
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JPanel buttonPanel;
|
||||
private javax.swing.JButton clusterMetricsButton;
|
||||
private javax.swing.Box.Filler filler1;
|
||||
private javax.swing.JButton healthMonitorButton;
|
||||
private javax.swing.JScrollPane nodeStatusScrollPane;
|
||||
private javax.swing.JPanel mainPanel;
|
||||
private javax.swing.JLabel nodeStatusTableTitle;
|
||||
private javax.swing.JButton refreshButton;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 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.experimental.autoingest;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
@ -73,7 +74,8 @@ final class AinStatusDashboardTopComponent extends TopComponent {
|
||||
AinStatusDashboard nodeTab = new AinStatusDashboard(monitor);
|
||||
nodeTab.startUp();
|
||||
nodeTab.setSize(nodeTab.getPreferredSize());
|
||||
tc.add(nodeTab);
|
||||
tc.setLayout(new BorderLayout());
|
||||
tc.add(nodeTab, BorderLayout.CENTER);
|
||||
tc.open();
|
||||
}
|
||||
}
|
||||
|
@ -22,61 +22,20 @@
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="pendingScrollPane" max="32767" attributes="0"/>
|
||||
<Component id="runningScrollPane" alignment="0" max="32767" attributes="0"/>
|
||||
<Component id="completedScrollPane" alignment="0" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="lbServicesStatus" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="tbServicesStatusMessage" pref="861" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="lbPending" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbCompleted" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbRunning" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="refreshButton" alignment="0" min="-2" pref="100" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="lbServicesStatus" alignment="3" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<Component id="tbServicesStatusMessage" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbPending" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
|
||||
<Component id="pendingScrollPane" max="32767" attributes="0"/>
|
||||
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbRunning" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
|
||||
<Component id="runningScrollPane" pref="133" max="32767" attributes="0"/>
|
||||
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="lbCompleted" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="completedScrollPane" pref="179" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="refreshButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="mainScrollPane">
|
||||
<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"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="mainPanel">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="pendingScrollPane">
|
||||
<Properties>
|
||||
@ -84,26 +43,51 @@
|
||||
<Property name="verticalScrollBarPolicy" type="int" value="21"/>
|
||||
<Property name="opaque" type="boolean" value="false"/>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[2, 215]"/>
|
||||
<Dimension value="[2, 150]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="3" gridWidth="2" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="10" insetsBottom="10" insetsRight="10" anchor="18" weightX="1.0" weightY="1.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="runningScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[2, 150]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="5" gridWidth="2" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="10" insetsBottom="10" insetsRight="10" anchor="18" weightX="1.0" weightY="1.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="completedScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[2, 150]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="7" gridWidth="5" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="10" insetsBottom="10" insetsRight="10" anchor="18" weightX="1.0" weightY="1.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
</Container>
|
||||
@ -118,6 +102,11 @@
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.lbPending.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="2" gridWidth="2" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="10" insetsLeft="10" insetsBottom="5" insetsRight="10" anchor="18" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="lbRunning">
|
||||
<Properties>
|
||||
@ -130,6 +119,11 @@
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.lbRunning.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="4" gridWidth="2" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="10" insetsBottom="5" insetsRight="10" anchor="18" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="lbCompleted">
|
||||
<Properties>
|
||||
@ -142,6 +136,11 @@
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.lbCompleted.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="6" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="10" insetsBottom="5" insetsRight="10" anchor="18" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="refreshButton">
|
||||
<Properties>
|
||||
@ -155,6 +154,11 @@
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="refreshButtonActionPerformed"/>
|
||||
</Events>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="8" gridWidth="2" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="10" insetsBottom="10" insetsRight="10" anchor="18" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="lbServicesStatus">
|
||||
<Properties>
|
||||
@ -167,6 +171,11 @@
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.lbServicesStatus.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="10" insetsLeft="10" insetsBottom="0" insetsRight="10" anchor="18" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="tbServicesStatusMessage">
|
||||
<Properties>
|
||||
@ -183,6 +192,15 @@
|
||||
<Border info="null"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="10" insetsLeft="0" insetsBottom="0" insetsRight="10" anchor="18" weightX="1.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -348,8 +348,11 @@ final class AutoIngestDashboard extends JPanel implements Observer {
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
java.awt.GridBagConstraints gridBagConstraints;
|
||||
|
||||
jButton1 = new javax.swing.JButton();
|
||||
mainScrollPane = new javax.swing.JScrollPane();
|
||||
mainPanel = new javax.swing.JPanel();
|
||||
pendingScrollPane = new javax.swing.JScrollPane();
|
||||
runningScrollPane = new javax.swing.JScrollPane();
|
||||
completedScrollPane = new javax.swing.JScrollPane();
|
||||
@ -362,19 +365,77 @@ final class AutoIngestDashboard extends JPanel implements Observer {
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.jButton1.text")); // NOI18N
|
||||
|
||||
setLayout(new java.awt.BorderLayout());
|
||||
|
||||
mainPanel.setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
pendingScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
pendingScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
|
||||
pendingScrollPane.setOpaque(false);
|
||||
pendingScrollPane.setPreferredSize(new java.awt.Dimension(2, 215));
|
||||
pendingScrollPane.setPreferredSize(new java.awt.Dimension(2, 150));
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 3;
|
||||
gridBagConstraints.gridwidth = 2;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
|
||||
gridBagConstraints.weightx = 1.0;
|
||||
gridBagConstraints.weighty = 1.0;
|
||||
gridBagConstraints.insets = new java.awt.Insets(0, 10, 10, 10);
|
||||
mainPanel.add(pendingScrollPane, gridBagConstraints);
|
||||
|
||||
runningScrollPane.setPreferredSize(new java.awt.Dimension(2, 150));
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 5;
|
||||
gridBagConstraints.gridwidth = 2;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
|
||||
gridBagConstraints.weightx = 1.0;
|
||||
gridBagConstraints.weighty = 1.0;
|
||||
gridBagConstraints.insets = new java.awt.Insets(0, 10, 10, 10);
|
||||
mainPanel.add(runningScrollPane, gridBagConstraints);
|
||||
|
||||
completedScrollPane.setPreferredSize(new java.awt.Dimension(2, 150));
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 7;
|
||||
gridBagConstraints.gridwidth = 5;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
|
||||
gridBagConstraints.weightx = 1.0;
|
||||
gridBagConstraints.weighty = 1.0;
|
||||
gridBagConstraints.insets = new java.awt.Insets(0, 10, 10, 10);
|
||||
mainPanel.add(completedScrollPane, gridBagConstraints);
|
||||
|
||||
lbPending.setFont(lbPending.getFont().deriveFont(lbPending.getFont().getSize()+3f));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(lbPending, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.lbPending.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 2;
|
||||
gridBagConstraints.gridwidth = 2;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
|
||||
gridBagConstraints.insets = new java.awt.Insets(10, 10, 5, 10);
|
||||
mainPanel.add(lbPending, gridBagConstraints);
|
||||
|
||||
lbRunning.setFont(lbRunning.getFont().deriveFont(lbRunning.getFont().getSize()+3f));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(lbRunning, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.lbRunning.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 4;
|
||||
gridBagConstraints.gridwidth = 2;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
|
||||
gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 10);
|
||||
mainPanel.add(lbRunning, gridBagConstraints);
|
||||
|
||||
lbCompleted.setFont(lbCompleted.getFont().deriveFont(lbCompleted.getFont().getSize()+3f));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(lbCompleted, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.lbCompleted.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 6;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
|
||||
gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 10);
|
||||
mainPanel.add(lbCompleted, gridBagConstraints);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(refreshButton, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.refreshButton.text")); // NOI18N
|
||||
refreshButton.setToolTipText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.refreshButton.toolTipText")); // NOI18N
|
||||
@ -383,61 +444,39 @@ final class AutoIngestDashboard extends JPanel implements Observer {
|
||||
refreshButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 8;
|
||||
gridBagConstraints.gridwidth = 2;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
|
||||
gridBagConstraints.insets = new java.awt.Insets(6, 10, 10, 10);
|
||||
mainPanel.add(refreshButton, gridBagConstraints);
|
||||
|
||||
lbServicesStatus.setFont(lbServicesStatus.getFont().deriveFont(lbServicesStatus.getFont().getSize()+3f));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(lbServicesStatus, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.lbServicesStatus.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 0;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
|
||||
gridBagConstraints.insets = new java.awt.Insets(10, 10, 0, 10);
|
||||
mainPanel.add(lbServicesStatus, gridBagConstraints);
|
||||
|
||||
tbServicesStatusMessage.setEditable(false);
|
||||
tbServicesStatusMessage.setFont(tbServicesStatusMessage.getFont().deriveFont(tbServicesStatusMessage.getFont().getStyle() | java.awt.Font.BOLD, tbServicesStatusMessage.getFont().getSize()+1));
|
||||
tbServicesStatusMessage.setText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.text")); // NOI18N
|
||||
tbServicesStatusMessage.setBorder(null);
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 1;
|
||||
gridBagConstraints.gridy = 0;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
|
||||
gridBagConstraints.weightx = 1.0;
|
||||
gridBagConstraints.insets = new java.awt.Insets(10, 0, 0, 10);
|
||||
mainPanel.add(tbServicesStatusMessage, gridBagConstraints);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(pendingScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(runningScrollPane, javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(completedScrollPane, javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
|
||||
.addComponent(lbServicesStatus)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(tbServicesStatusMessage, javax.swing.GroupLayout.DEFAULT_SIZE, 861, Short.MAX_VALUE))
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(lbPending, javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(lbCompleted, javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(lbRunning, javax.swing.GroupLayout.Alignment.LEADING))
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.addComponent(refreshButton, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(lbServicesStatus, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(tbServicesStatusMessage, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(lbPending, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(1, 1, 1)
|
||||
.addComponent(pendingScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(lbRunning)
|
||||
.addGap(1, 1, 1)
|
||||
.addComponent(runningScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 133, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(lbCompleted)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(completedScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 179, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(refreshButton)
|
||||
.addContainerGap())
|
||||
);
|
||||
mainScrollPane.setViewportView(mainPanel);
|
||||
|
||||
add(mainScrollPane, java.awt.BorderLayout.CENTER);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
/**
|
||||
@ -460,6 +499,8 @@ final class AutoIngestDashboard extends JPanel implements Observer {
|
||||
private javax.swing.JLabel lbPending;
|
||||
private javax.swing.JLabel lbRunning;
|
||||
private javax.swing.JLabel lbServicesStatus;
|
||||
private javax.swing.JPanel mainPanel;
|
||||
private javax.swing.JScrollPane mainScrollPane;
|
||||
private javax.swing.JScrollPane pendingScrollPane;
|
||||
private javax.swing.JButton refreshButton;
|
||||
private javax.swing.JScrollPane runningScrollPane;
|
||||
|
@ -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");
|
||||
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.EventQueue;
|
||||
import java.util.List;
|
||||
@ -69,13 +70,14 @@ public final class AutoIngestDashboardTopComponent extends TopComponent {
|
||||
* dashboard instance so we don't accumulate them.
|
||||
*/
|
||||
tc.removeAll();
|
||||
tc.setLayout(new BorderLayout());
|
||||
|
||||
/*
|
||||
* Create a new dashboard instance to ensure we're using the
|
||||
* most recent configuration.
|
||||
*/
|
||||
AutoIngestDashboard dashboard = AutoIngestDashboard.createDashboard();
|
||||
tc.add(dashboard);
|
||||
tc.add(dashboard, BorderLayout.CENTER);
|
||||
dashboard.setSize(dashboard.getPreferredSize());
|
||||
//if the user has administrator access enabled open the Node Status and cases top components as well
|
||||
if (AutoIngestDashboard.isAdminAutoIngestDashboard()) {
|
||||
|
@ -205,7 +205,9 @@ DeleteCaseTask.progress.parsingManifest=Parsing manifest file {0}...
|
||||
DeleteCaseTask.progress.releasingManifestLock=Releasing lock on the manifest file {0}...
|
||||
DeleteCaseTask.progress.startMessage=Starting deletion...
|
||||
DeleteOrphanCaseNodesAction.progressDisplayName=Cleanup Case Znodes
|
||||
# {0} - item count
|
||||
DeleteOrphanCaseNodesDialog.additionalInit.lblNodeCount.text=Znodes found: {0}
|
||||
# {0} - item count
|
||||
DeleteOrphanCaseNodesDialog.additionalInit.znodesTextArea.countMessage=ZNODES FOUND: {0}
|
||||
DeleteOrphanCaseNodesTask.progress.connectingToCoordSvc=Connecting to the coordination service
|
||||
# {0} - node path
|
||||
|
@ -13,43 +13,40 @@
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="refreshButton" linkSize="3" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="deleteOrphanCaseNodesButton" linkSize="3" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="deleteOrphanManifestNodesButton" linkSize="3" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="caseBrowserScrollPane" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</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="caseBrowserScrollPane" pref="246" max="32767" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="refreshButton" linkSize="2" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="deleteOrphanCaseNodesButton" linkSize="2" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="deleteOrphanManifestNodesButton" linkSize="2" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="caseBrowserScrollPane">
|
||||
<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"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="mainPanel">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="bottomPanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="South"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="buttonPanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="10" insetsLeft="10" insetsBottom="10" insetsRight="10" anchor="18" weightX="1.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridLayout">
|
||||
<Property name="columns" type="int" value="0"/>
|
||||
<Property name="rows" type="int" value="1"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="refreshButton">
|
||||
@ -62,10 +59,6 @@
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="refreshButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="caseBrowserScrollPane">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
</Container>
|
||||
<Component class="javax.swing.JButton" name="deleteOrphanCaseNodesButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
@ -87,4 +80,12 @@
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019-2019 Basis Technology Corp.
|
||||
* Copyright 2019-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.explorer.ExplorerUtils;
|
||||
@ -95,8 +96,7 @@ public final class CasesDashboardTopComponent extends TopComponent implements Ex
|
||||
explorerManager = new ExplorerManager();
|
||||
associateLookup(ExplorerUtils.createLookup(explorerManager, getActionMap()));
|
||||
caseBrowserPanel = new MultiUserCasesBrowserPanel(explorerManager, new CasesDashboardCustomizer());
|
||||
caseBrowserScrollPane.add(caseBrowserPanel);
|
||||
caseBrowserScrollPane.setViewportView(caseBrowserPanel);
|
||||
mainPanel.add(caseBrowserPanel, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -116,18 +116,31 @@ public final class CasesDashboardTopComponent extends TopComponent implements Ex
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
java.awt.GridBagConstraints gridBagConstraints;
|
||||
|
||||
refreshButton = new javax.swing.JButton();
|
||||
caseBrowserScrollPane = new javax.swing.JScrollPane();
|
||||
mainPanel = new javax.swing.JPanel();
|
||||
bottomPanel = new javax.swing.JPanel();
|
||||
buttonPanel = new javax.swing.JPanel();
|
||||
refreshButton = new javax.swing.JButton();
|
||||
deleteOrphanCaseNodesButton = new javax.swing.JButton();
|
||||
deleteOrphanManifestNodesButton = new javax.swing.JButton();
|
||||
|
||||
setLayout(new java.awt.BorderLayout());
|
||||
|
||||
mainPanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
bottomPanel.setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
buttonPanel.setLayout(new java.awt.GridLayout());
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(refreshButton, org.openide.util.NbBundle.getMessage(CasesDashboardTopComponent.class, "CasesDashboardTopComponent.refreshButton.text")); // NOI18N
|
||||
refreshButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
refreshButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
buttonPanel.add(refreshButton);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(deleteOrphanCaseNodesButton, org.openide.util.NbBundle.getMessage(CasesDashboardTopComponent.class, "CasesDashboardTopComponent.deleteOrphanCaseNodesButton.text")); // NOI18N
|
||||
deleteOrphanCaseNodesButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
@ -135,6 +148,7 @@ public final class CasesDashboardTopComponent extends TopComponent implements Ex
|
||||
deleteOrphanCaseNodesButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
buttonPanel.add(deleteOrphanCaseNodesButton);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(deleteOrphanManifestNodesButton, org.openide.util.NbBundle.getMessage(CasesDashboardTopComponent.class, "CasesDashboardTopComponent.deleteOrphanManifestNodesButton.text")); // NOI18N
|
||||
deleteOrphanManifestNodesButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
@ -142,43 +156,21 @@ public final class CasesDashboardTopComponent extends TopComponent implements Ex
|
||||
deleteOrphanManifestNodesButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
buttonPanel.add(deleteOrphanManifestNodesButton);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(refreshButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(deleteOrphanCaseNodesButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(deleteOrphanManifestNodesButton)
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(caseBrowserScrollPane)
|
||||
.addContainerGap())))
|
||||
);
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 0;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
|
||||
gridBagConstraints.weightx = 1.0;
|
||||
gridBagConstraints.insets = new java.awt.Insets(10, 10, 10, 10);
|
||||
bottomPanel.add(buttonPanel, gridBagConstraints);
|
||||
|
||||
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {deleteOrphanCaseNodesButton, deleteOrphanManifestNodesButton, refreshButton});
|
||||
mainPanel.add(bottomPanel, java.awt.BorderLayout.SOUTH);
|
||||
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(caseBrowserScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 246, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(refreshButton)
|
||||
.addComponent(deleteOrphanCaseNodesButton)
|
||||
.addComponent(deleteOrphanManifestNodesButton))
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {deleteOrphanCaseNodesButton, deleteOrphanManifestNodesButton, refreshButton});
|
||||
caseBrowserScrollPane.setViewportView(mainPanel);
|
||||
|
||||
add(caseBrowserScrollPane, java.awt.BorderLayout.CENTER);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_refreshButtonActionPerformed
|
||||
@ -194,9 +186,12 @@ public final class CasesDashboardTopComponent extends TopComponent implements Ex
|
||||
}//GEN-LAST:event_deleteOrphanManifestNodesButtonActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JPanel bottomPanel;
|
||||
private javax.swing.JPanel buttonPanel;
|
||||
private javax.swing.JScrollPane caseBrowserScrollPane;
|
||||
private javax.swing.JButton deleteOrphanCaseNodesButton;
|
||||
private javax.swing.JButton deleteOrphanManifestNodesButton;
|
||||
private javax.swing.JPanel mainPanel;
|
||||
private javax.swing.JButton refreshButton;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
|
@ -10,3 +10,5 @@ MemoryDSInputPanel.errorLabel.text=Error Label
|
||||
MemoryDSInputPanel.browseButton.text=Browse
|
||||
MemoryDSInputPanel.timeZoneLabel.text=Timezone:
|
||||
MemoryDSInputPanel.profileLabel.text=Profile:
|
||||
MemoryDSInputPanel.selectAllButton.text=Select All
|
||||
MemoryDSInputPanel.deselectAllButton.text=Deselect All
|
||||
|
@ -17,6 +17,8 @@ MemoryDSInputPanel.errorLabel.text=Error Label
|
||||
MemoryDSInputPanel.browseButton.text=Browse
|
||||
MemoryDSInputPanel.timeZoneLabel.text=Timezone:
|
||||
MemoryDSInputPanel.profileLabel.text=Profile:
|
||||
MemoryDSInputPanel.selectAllButton.text=Select All
|
||||
MemoryDSInputPanel.deselectAllButton.text=Deselect All
|
||||
MemoryDSInputPanel_errorMsg_dataSourcePathOnCdrive=Path to multi-user data source is on "C:" drive
|
||||
MemoryDSInputPanel_errorMsg_noOpenCase=No open case
|
||||
MemoryDSProcessor.dataSourceType=Memory Image File (Volatility)
|
||||
|
@ -37,6 +37,11 @@
|
||||
<Component id="profileComboBox" alignment="0" pref="243" max="32767" attributes="0"/>
|
||||
<Component id="timeZoneComboBox" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="selectAllButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="deselectAllButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
@ -78,7 +83,12 @@
|
||||
<Component id="PluginsToRunLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="listsScrollPane" min="-2" pref="122" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="73" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="selectAllButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="deselectAllButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="44" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -188,5 +198,25 @@
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="selectAllButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/volatilityDSP/Bundle.properties" key="MemoryDSInputPanel.selectAllButton.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="selectAllButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="deselectAllButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/volatilityDSP/Bundle.properties" key="MemoryDSInputPanel.deselectAllButton.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="deselectAllButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -191,6 +191,8 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
||||
pluginTable = new javax.swing.JTable();
|
||||
profileLabel = new javax.swing.JLabel();
|
||||
profileComboBox = new javax.swing.JComboBox<>();
|
||||
selectAllButton = new javax.swing.JButton();
|
||||
deselectAllButton = new javax.swing.JButton();
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(MemoryDSInputPanel.class, "MemoryDSInputPanel.pathLabel.text")); // NOI18N
|
||||
|
||||
@ -238,6 +240,20 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(selectAllButton, org.openide.util.NbBundle.getMessage(MemoryDSInputPanel.class, "MemoryDSInputPanel.selectAllButton.text")); // NOI18N
|
||||
selectAllButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
selectAllButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(deselectAllButton, org.openide.util.NbBundle.getMessage(MemoryDSInputPanel.class, "MemoryDSInputPanel.deselectAllButton.text")); // NOI18N
|
||||
deselectAllButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
deselectAllButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
@ -256,7 +272,11 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
||||
.addComponent(listsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 248, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
|
||||
.addComponent(profileComboBox, javax.swing.GroupLayout.Alignment.LEADING, 0, 243, Short.MAX_VALUE)
|
||||
.addComponent(timeZoneComboBox, javax.swing.GroupLayout.Alignment.LEADING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))))
|
||||
.addComponent(timeZoneComboBox, javax.swing.GroupLayout.Alignment.LEADING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(selectAllButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(deselectAllButton)))))
|
||||
.addGap(0, 163, Short.MAX_VALUE))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
@ -287,7 +307,11 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(PluginsToRunLabel)
|
||||
.addComponent(listsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 122, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addContainerGap(73, Short.MAX_VALUE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(selectAllButton)
|
||||
.addComponent(deselectAllButton))
|
||||
.addContainerGap(44, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
pathLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(MemoryDSInputPanel.class, "MemoryDSInputPanel.pathLabel.AccessibleContext.accessibleName")); // NOI18N
|
||||
@ -316,9 +340,24 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
||||
// TODO add your handling code here:
|
||||
}//GEN-LAST:event_pathTextFieldActionPerformed
|
||||
|
||||
private void selectAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectAllButtonActionPerformed
|
||||
for(String name : pluginListStates.keySet()) {
|
||||
pluginListStates.put(name, Boolean.TRUE);
|
||||
}
|
||||
tableModel.fireTableDataChanged();
|
||||
}//GEN-LAST:event_selectAllButtonActionPerformed
|
||||
|
||||
private void deselectAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deselectAllButtonActionPerformed
|
||||
for(String name : pluginListStates.keySet()) {
|
||||
pluginListStates.put(name, Boolean.FALSE);
|
||||
}
|
||||
tableModel.fireTableDataChanged();
|
||||
}//GEN-LAST:event_deselectAllButtonActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel PluginsToRunLabel;
|
||||
private javax.swing.JButton browseButton;
|
||||
private javax.swing.JButton deselectAllButton;
|
||||
private javax.swing.JLabel errorLabel;
|
||||
private javax.swing.ButtonGroup infileTypeButtonGroup;
|
||||
private javax.swing.JScrollPane listsScrollPane;
|
||||
@ -327,6 +366,7 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
|
||||
private javax.swing.JTable pluginTable;
|
||||
private javax.swing.JComboBox<String> profileComboBox;
|
||||
private javax.swing.JLabel profileLabel;
|
||||
private javax.swing.JButton selectAllButton;
|
||||
private javax.swing.JComboBox<String> timeZoneComboBox;
|
||||
private javax.swing.JLabel timeZoneLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
1
NEWS.txt
1
NEWS.txt
@ -33,6 +33,7 @@ Reporting:
|
||||
|
||||
Misc:
|
||||
- Added support for Ext4 inline data and sparse blocks (via TSK fix).
|
||||
- Fixed timeline controller deadlock issue
|
||||
- Updated PostgreSQL JDBC driver to support any recent version of PostgreSQL for multi-user cases and PostgreSQL Central Repository.
|
||||
- Added personas to the summary viewer in CVT.
|
||||
- Handling of bad characters in auto ingest manifest files.
|
||||
|
@ -876,7 +876,10 @@ class ExtractRegistry extends Extract {
|
||||
try{
|
||||
createOrUpdateOsAccount(regFile, sid, username, homeDir);
|
||||
|
||||
} catch(TskCoreException | TskDataException ex) {
|
||||
} catch (OsAccountManager.NotUserSIDException ex) {
|
||||
logger.log(Level.WARNING, String.format("Cannot create OsAccount for file: %s, sid: %s is not a user SID.", regFile.getId(), sid));
|
||||
}
|
||||
catch(TskCoreException | TskDataException ex ) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to create OsAccount for file: %s, sid: %s", regFile.getId(), sid));
|
||||
}
|
||||
|
||||
@ -1216,7 +1219,10 @@ class ExtractRegistry extends Extract {
|
||||
logger.log(Level.WARNING, "Error parsing the the date from the registry file", ex); //NON-NLS
|
||||
} catch (TskDataException | TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error updating TSK_OS_ACCOUNT artifacts to include newly parsed data.", ex); //NON-NLS
|
||||
} finally {
|
||||
} catch (OsAccountManager.NotUserSIDException ex) {
|
||||
logger.log(Level.WARNING, "Error creating OS Account, input SID is not a user SID.", ex); //NON-NLS
|
||||
}
|
||||
finally {
|
||||
if (!context.dataSourceIngestIsCancelled()) {
|
||||
postArtifacts(newArtifacts);
|
||||
}
|
||||
@ -2206,8 +2212,9 @@ class ExtractRegistry extends Extract {
|
||||
*
|
||||
* @throws TskCoreException
|
||||
* @throws TskDataException
|
||||
* @throws OsAccountManager.NotUserSIDException
|
||||
*/
|
||||
private void createOrUpdateOsAccount(AbstractFile file, String sid, String userName, String homeDir) throws TskCoreException, TskDataException {
|
||||
private void createOrUpdateOsAccount(AbstractFile file, String sid, String userName, String homeDir) throws TskCoreException, TskDataException, OsAccountManager.NotUserSIDException {
|
||||
OsAccountManager accountMgr = tskCase.getOsAccountManager();
|
||||
HostManager hostMrg = tskCase.getHostManager();
|
||||
Host host = hostMrg.getHost((DataSource)dataSource);
|
||||
|
@ -173,7 +173,7 @@
|
||||
<!-- Update configuration file to include jre -->
|
||||
<property name="inst.property.file" value="${inst-path}/etc/${app.name}.conf" />
|
||||
<!-- Sets max heap size to be ${jvm.max.mem} which is set in the run-ai-(32/64) target -->
|
||||
<var name="jvm.args" value=""--branding ${app.name} -J-Xms24m -J-Xmx${jvm.max.mem} -J-XX:MaxPermSize=128M -J-Xverify:none -J-XX:+UseG1GC -J-XX:+UseStringDeduplication "" />
|
||||
<var name="jvm.args" value=""--branding ${app.name} -J-Xms24m -J-Xmx4G -J-Xverify:none -J-XX:+UseG1GC -J-XX:+UseStringDeduplication -J-Dprism.order=sw "" />
|
||||
<propertyfile file="${inst.property.file}">
|
||||
<!-- Note: can be higher on 64 bit systems, should be in sync with project.properties -->
|
||||
<entry key="default_options" value="@JVM_OPTIONS" />
|
||||
|
BIN
docs/doxygen-user/images/solr/solr_config_monitoring.png
Normal file
BIN
docs/doxygen-user/images/solr/solr_config_monitoring.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 130 KiB |
Binary file not shown.
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 97 KiB |
@ -53,9 +53,8 @@ Follow these steps to configure Solr:
|
||||
Required Solr Configuration Parameters:
|
||||
<ul>
|
||||
<li><b>JAVA_HOME</b> – path to 64-bit JRE installation. For example \c "JAVA_HOME=C:\Program Files\Java\jre1.8.0_151" or \c "JAVA_HOME=C:\Program Files\ojdkbuild\java-1.8.0-openjdk-1.8.0.222-1"
|
||||
<li><b>DEFAULT_CONFDIR</b> – path to Autopsy configuration directory. If the Solr archive was extracted into \c "C:\solr-8.6.3" directory, then this path will be \c "C:\ solr-8.6.3\server\solr\configsets\AutopsyConfig\conf".
|
||||
<li><b>Dbootstrap_confdir</b> – same path as <b>DEFAULT_CONFDIR</b>
|
||||
<li><b>SOLR_JAVA_MEM</b> - Solr JVM heap size should be somewhere between one third and one half of the total RAM available on the machine. A rule of thumb would be use \c "set SOLR_JAVA_MEM=-Xms2G -Xmx14G" for a machine with 32GB of RAM or more, and \c "set SOLR_JAVA_MEM=-Xms2G -Xmx8G" for a machine with 16GB of RAM.
|
||||
<li><b>DEFAULT_CONFDIR</b> – path to Autopsy configuration directory. If the Solr archive was extracted into \c "C:\solr-8.6.3" directory, then this path will be \c "C:\ solr-8.6.3\server\solr\configsets\AutopsyConfig\conf". Do not include quotes around the path.
|
||||
<li><b>SOLR_JAVA_MEM</b> - Solr JVM heap size should be as large as the Solr machine’s resources allow, at least half of the total RAM available on the machine. A rule of thumb would be use "set SOLR_JAVA_MEM=-Xms2G -Xmx40G" for a machine with 64GB of RAM, "set SOLR_JAVA_MEM=-Xms2G -Xmx20G" for a machine with 32GB of RAM, and "set SOLR_JAVA_MEM=-Xms2G -Xmx8G" for a machine with 16GB of RAM. Please see the \ref install_solr_heap_usage "troubleshooting section" for more info regarding Solr heap usage and troubleshooting information.
|
||||
<li><b>SOLR_DATA_HOME</b> – location where Solr indexes will be stored. If this is not configured, the indexes will be stored in the \c "C:\solr-8.6.3\server\solr" directory. NOTE: for Autopsy cases consisting of large number of data sources, Solr indexes can get very large (hundreds of GBs, or TBs) so they should probably be stored on a larger network share.
|
||||
</ul>
|
||||
|
||||
@ -208,8 +207,57 @@ Solr creates two types of data that need to be backed up:
|
||||
<ol><li>In a default installation that data is stored in \c "C:\solr-8.6.3\server\solr zoo_data" (assuming that the Solr package ZIP was extracted into \c "C:\solr-8.6.3" directory).</ol>
|
||||
</ul>
|
||||
|
||||
\section install_solr_delayed_start Delayed Start Problems With Large Number Of Solr Collections
|
||||
\section Troubleshooting
|
||||
|
||||
\subsection install_solr_delayed_start Delayed Start Problems With Large Number Of Solr Collections
|
||||
|
||||
In our testing, we have encountered an issue when a very large number (thousands) of Autopsy multi-user cases was created. Each new Autopsy multi-user case creates a Solr "collection" that contains the Solr text index. With 2,000 existing collections, when Solr service is restarted, Solr appears to internally be "loading" roughly 250 collections per minute (in chronological order, starting with oldest collections). After 4 minutes roughly half of the 2,000 collections were loaded. Users are able to search the collections that have been loaded, but they are unable to open or search the collections that have not yet been internally loaded by Solr. After 7-8 minutes all collections were loaded. These numbers will vary depending on the specific cluster configuration, text index file location (network or local storage), network throughput, number of Solr servers, etc.
|
||||
|
||||
\subsection install_solr_heap_usage Solr Heap Usage and Recommendations
|
||||
|
||||
Solr JVM heap plays especially important role if you are going to create a large number of Autopsy cases (i.e. Solr collections). Here are some “rule of thumb” Solr heap usage stats that we identified during our internal testing:
|
||||
<ul>
|
||||
<li>For very small cases/collections, our tests show that Solr uses an absolute minimum of 7-10 MB of heap per collection.
|
||||
<li>For larger cases/collections (50-100GB input E01 size) Solr uses at least 65 MB per collection
|
||||
<li>For large cases/collections (1.5TB input E01 size) Solr uses at least 850 MB per collection
|
||||
</ul>
|
||||
|
||||
\subsubsection install_solr_heap_troublshooting Troubleshooting Solr Heap Issues
|
||||
|
||||
Once the Solr JVM uses all of its available heap and is unable to free up any memory via garbage collection, the Solr service will not be able to create new collections or may become completely unresponsive, resulting in Autopsy being unable to create new text indexes. Below is a list of some of the errors that you might see as a result of this in the Solr (not Autopsy) service logs and/or the Solr admin console:
|
||||
|
||||
<ul>
|
||||
<li>org.apache.solr.common.SolrException: Could not register as the leader because creating the ephemeral registration node in ZooKeeper failed
|
||||
<li>RequestHandlerBase org.apache.solr.common.SolrException: Failed to get config from zookeeper
|
||||
<li>RecoveryStrategy Error while trying to recover. org.apache.solr.common.SolrException: Cloud state still says we are leader.
|
||||
<li>RequestHandlerBase org.apache.solr.common.SolrException: Could not load collection from ZK
|
||||
<li>org.apache.solr.common.SolrException: Error CREATEing SolrCore: Unable to create core. Caused by: There are no more files
|
||||
<li>org.apache.solr.common.SolrException: java.io.IOException: There are no more files
|
||||
<li>org.apache.solr.common.SolrException: Cannot unload non-existent core
|
||||
<li>ZkIndexSchemaReader Error creating ZooKeeper watch for the managed schema
|
||||
</ul>
|
||||
|
||||
You may also see the following ZooKeeper errors:
|
||||
<ul>
|
||||
<li>org.apache.zookeeper.KeeperException$NodeExistsException: KeeperErrorCode = NodeExists
|
||||
<li>org.apache.zookeeper.KeeperException$BadVersionException: KeeperErrorCode = BadVersion for (collection_name)/state.json
|
||||
<li>org.apache.zookeeper.KeeperException$SessionExpiredException: KeeperErrorCode = Session expired for /roles.json
|
||||
<li>org.apache.zookeeper.KeeperException$SessionExpiredException: KeeperErrorCode = Session expired for /configs/AutopsyConfig/managed-schema
|
||||
</ul>
|
||||
|
||||
The common theme among most of these errors is the breakdown in communication between Solr and ZooKeeper, especially when using an embedded ZooKeeper server. It is important to note that these errors may potentially occur for other reasons and are not unique to Solr heap issues.
|
||||
|
||||
\subsubsection install_solr_monitoring Monitoring Solr Heap Usage
|
||||
|
||||
The simplest way to see current Solr heap usage is to check the Solr Admin Console web page. To access the Solr admin console, on the Solr machine navigate to http://localhost:8983/solr/#/ . There you will be able to see the Solr memory usage:
|
||||
|
||||
\image html solr_config_monitoring.png
|
||||
|
||||
However, the dashboard does not show enough detail to know when Solr is out of heap, so it should only be used to identify that you are NOT having heap issues. Even if the dashboard shows that the Solr heap is fully used, it may or may not be an issue. It is best to use profiling tools like Java VisualVM. In order for VisualVM to connect to Solr, you will need to enable the JMX interface for Solr’s Java process. The details are described here:
|
||||
<ul><li>https://solr.apache.org/guide/8_3/using-jmx-with-solr.html#using-jmx-with-solr</ul>
|
||||
|
||||
Solr heap and other performance tuning is described in the following article:
|
||||
<ul><li>https://cwiki.apache.org/confluence/display/SOLR/SolrPerformanceProblems</ul>
|
||||
|
||||
|
||||
*/
|
||||
|
@ -7,9 +7,9 @@ suite.dir=${basedir}
|
||||
nbplatform.active=download
|
||||
nbplatform.active.dir=${suite.dir}/netbeans-plat/${netbeans-plat-version}
|
||||
harness.dir=${nbplatform.active.dir}/harness
|
||||
bootstrap.url=https://netbeans-vm.apache.org/uc/${netbeans-plat-version}/tasks.jar
|
||||
bootstrap.url=https://netbeans-vm1.apache.org/uc/${netbeans-plat-version}/tasks.jar
|
||||
# Where we get the platform from. To see what versions are available, open URL in browser up to the .../updates part of the URL
|
||||
autoupdate.catalog.url=https://netbeans-vm.apache.org/uc/${netbeans-plat-version}/updates.xml.gz
|
||||
autoupdate.catalog.url=https://netbeans-vm1.apache.org/uc/${netbeans-plat-version}/updates.xml.gz
|
||||
cluster.path=\
|
||||
${nbplatform.active.dir}/harness:\
|
||||
${nbplatform.active.dir}/java:\
|
||||
|
Loading…
x
Reference in New Issue
Block a user