Merge remote-tracking branch 'upstream/develop' into 7393_hostDoc

This commit is contained in:
apriestman 2021-04-14 07:59:29 -04:00
commit 7f53869448
195 changed files with 7360 additions and 2702 deletions

View File

@ -89,6 +89,11 @@
<fileset dir="${thirdparty.dir}/ImageMagick-7.0.10-27-portable-Q16-x64"/>
</copy>
<!--Copy DomainCategorization to release-->
<copy todir="${basedir}/release/DomainCategorization" >
<fileset dir="${thirdparty.dir}/DomainCategorization"/>
</copy>
<!-- The 'libgstlibav.dll' file is too big to store on GitHub, so we
have it stored in a ZIP file. We'll extract it in place and remove
the ZIP file afterward. -->

View File

@ -0,0 +1,4 @@
CTL_ResetWindowsAction=Reset Windows
ResetWindowAction.caseCloseFailure.text=Unable to close the current case, the software will restart and the windows locations will reset the next time the software is closed.
ResetWindowAction.caseSaveMetadata.text=Unable to save current case path, the software will restart and the windows locations will reset but the current case will not be opened upon restart.
ResetWindowAction.confirm.text=In order to perform the resetting of window locations the software will close and restart. If a case is currently open, it will be closed. If ingest or a search is currently running, it will be terminated. Are you sure you want to restart the software to reset all window locations?

View File

@ -0,0 +1,141 @@
/*
* Autopsy
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.apputils;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.logging.Level;
import javax.swing.SwingUtilities;
import org.apache.commons.io.FileUtils;
import org.openide.LifecycleManager;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionRegistration;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CaseActionException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
/**
* Class to open the Discovery dialog. Allows the user to run searches and see
* results in the DiscoveryTopComponent.
*/
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.apputils.ResetWindowsAction")
@ActionReferences(value = {
@ActionReference(path = "Menu/Window", position = 105)})
@ActionRegistration(displayName = "#CTL_ResetWindowsAction", lazy = false)
@NbBundle.Messages({"CTL_ResetWindowsAction=Reset Windows"})
public final class ResetWindowsAction extends CallableSystemAction {
private static final String DISPLAY_NAME = Bundle.CTL_ResetWindowsAction();
private static final long serialVersionUID = 1L;
private final static Logger logger = Logger.getLogger(ResetWindowsAction.class.getName());
private final static String WINDOWS2LOCAL = "Windows2Local";
private final static String CASE_TO_REOPEN_FILE = "caseToOpen.txt";
@Override
public boolean isEnabled() {
return true;
}
@NbBundle.Messages({"ResetWindowAction.confirm.text=In order to perform the resetting of window locations the software will close and restart. "
+ "If a case is currently open, it will be closed. If ingest or a search is currently running, it will be terminated. "
+ "Are you sure you want to restart the software to reset all window locations?",
"ResetWindowAction.caseCloseFailure.text=Unable to close the current case, "
+ "the software will restart and the windows locations will reset the next time the software is closed.",
"ResetWindowAction.caseSaveMetadata.text=Unable to save current case path, "
+ "the software will restart and the windows locations will reset but the current case will not be opened upon restart."})
@Override
public void performAction() {
SwingUtilities.invokeLater(() -> {
boolean response = MessageNotifyUtil.Message.confirm(Bundle.ResetWindowAction_confirm_text());
if (response) {
//adding the shutdown hook, closing the current case, and marking for restart can be re-ordered if slightly different behavior is desired
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
FileUtils.deleteDirectory(new File(PlatformUtil.getUserConfigDirectory() + File.separator + WINDOWS2LOCAL));
} catch (IOException ex) {
//While we would like the user to be aware of this in the unlikely event that the directory can not be deleted
//Because our deletion is being attempted in a shutdown hook I don't know that we can pop up UI elements during the shutdown proces
logger.log(Level.SEVERE, "Unable to delete config directory, window locations will not be reset. To manually reset the windows please delete the following directory while the software is closed. " + PlatformUtil.getUserConfigDirectory() + File.separator + "Windows2Local", ex);
}
}
});
try {
if (Case.isCaseOpen()) {
String caseMetadataFilePath = Case.getCurrentCase().getMetadata().getFilePath().toString();
File caseToOpenFile = new File(ResetWindowsAction.getCaseToReopenFilePath());
Charset encoding = null; //prevents writeStringToFile from having ambiguous arguments
FileUtils.writeStringToFile(caseToOpenFile, caseMetadataFilePath, encoding);
Case.closeCurrentCase();
}
// The method markForRestart can not be undone once it is called.
LifecycleManager.getDefault().markForRestart();
//we need to call exit last
LifecycleManager.getDefault().exit();
} catch (CaseActionException ex) {
logger.log(Level.WARNING, Bundle.ResetWindowAction_caseCloseFailure_text(), ex);
MessageNotifyUtil.Message.show(Bundle.ResetWindowAction_caseCloseFailure_text(), MessageNotifyUtil.MessageType.ERROR);
} catch (IOException ex) {
logger.log(Level.WARNING, Bundle.ResetWindowAction_caseSaveMetadata_text(), ex);
MessageNotifyUtil.Message.show(Bundle.ResetWindowAction_caseSaveMetadata_text(), MessageNotifyUtil.MessageType.ERROR);
}
}
});
}
public static String getCaseToReopenFilePath(){
return PlatformUtil.getUserConfigDirectory() + File.separator + CASE_TO_REOPEN_FILE;
}
/**
* Set this action to be enabled/disabled
*
* @param value whether to enable this action or not
*/
@Override
public void setEnabled(boolean value) {
super.setEnabled(value);
}
@Override
public String getName() {
return DISPLAY_NAME;
}
@Override
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
@Override
public boolean asynchronous() {
return false; // run on edt
}
}

View File

@ -79,7 +79,7 @@ class AddImageWizardSelectHostVisual extends javax.swing.JPanel {
@Override
public int hashCode() {
int hash = 7;
hash = 41 * hash + Objects.hashCode(this.host == null ? 0 : this.host.getId());
hash = 41 * hash + Objects.hashCode(this.host == null ? 0 : this.host.getHostId());
return hash;
}
@ -96,8 +96,8 @@ class AddImageWizardSelectHostVisual extends javax.swing.JPanel {
}
final HostListItem other = (HostListItem) obj;
if (!Objects.equals(
this.host == null ? 0 : this.host.getId(),
other.host == null ? 0 : other.host.getId())) {
this.host == null ? 0 : this.host.getHostId(),
other.host == null ? 0 : other.host.getHostId())) {
return false;
}
@ -166,7 +166,7 @@ class AddImageWizardSelectHostVisual extends javax.swing.JPanel {
if (specifyNewHostRadio.isSelected() && StringUtils.isNotEmpty(specifyNewHostTextField.getText())) {
String newHostName = specifyNewHostTextField.getText();
try {
return Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().createHost(newHostName);
return Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().newHost(newHostName);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.WARNING, String.format("Unable to create host '%s'.", newHostName), ex);
return null;
@ -186,7 +186,7 @@ class AddImageWizardSelectHostVisual extends javax.swing.JPanel {
*/
private void loadHostData() {
try {
Collection<Host> hosts = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getHosts();
Collection<Host> hosts = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getAllHosts();
sanitizedHostSet = HostNameValidator.getSanitizedHostNames(hosts);
Vector<HostListItem> hostListItems = hosts.stream()

View File

@ -346,6 +346,12 @@ RecentCases.getName.text=Clear Recent Cases
RecentItems.openRecentCase.msgDlg.text=Case {0} no longer exists.
SelectDataSourceProcessorPanel.name.text=Select Data Source Type
StartupWindow.title.text=Welcome
# {0} - autFilePath
StartupWindowProvider.openCase.cantOpen=Unable to open previously open case with metadata file: {0}
# {0} - reOpenFilePath
StartupWindowProvider.openCase.deleteOpenFailure=Unable to open or delete file containing path {0} to previously open case. The previous case will not be opened.
# {0} - autFilePath
StartupWindowProvider.openCase.noFile=Unable to open previously open case because metadata file not found at: {0}
UnpackagePortableCaseDialog.title.text=Unpackage Portable Case
UnpackagePortableCaseDialog.UnpackagePortableCaseDialog.extensions=Portable case package (.zip, .zip.001)
UnpackagePortableCaseDialog.validatePaths.badExtension=File extension must be .zip or .zip.001

View File

@ -87,9 +87,10 @@ import org.sleuthkit.autopsy.casemodule.events.HostsChangedEvent;
import org.sleuthkit.autopsy.casemodule.events.HostsRemovedEvent;
import org.sleuthkit.autopsy.casemodule.events.OsAccountAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent;
import org.sleuthkit.autopsy.casemodule.events.OsAccountDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.PersonsAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.PersonsChangedEvent;
import org.sleuthkit.autopsy.casemodule.events.PersonsRemovedEvent;
import org.sleuthkit.autopsy.casemodule.events.PersonsDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException;
import org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils;
@ -139,23 +140,16 @@ import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.FileSystem;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.HostManager.HostsCreationEvent;
import org.sleuthkit.datamodel.HostManager.HostsUpdateEvent;
import org.sleuthkit.datamodel.HostManager.HostsDeletionEvent;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.OsAccountManager.OsAccountsCreationEvent;
import org.sleuthkit.datamodel.OsAccountManager.OsAccountsUpdateEvent;
import org.sleuthkit.datamodel.Person;
import org.sleuthkit.datamodel.PersonManager.PersonsCreationEvent;
import org.sleuthkit.datamodel.PersonManager.PersonsUpdateEvent;
import org.sleuthkit.datamodel.PersonManager.PersonsDeletionEvent;
import org.sleuthkit.datamodel.Report;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TimelineManager;
import org.sleuthkit.datamodel.SleuthkitCaseAdminUtil;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException;
import org.sleuthkit.datamodel.TskEvent;
import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
/**
@ -438,41 +432,38 @@ public class Case {
*/
OS_ACCOUNT_ADDED,
/**
* OSAccount associated with the current case has changed.
* Call getOsAccount to get the changed account;
* OSAccount associated with the current case has changed. Call
* getOsAccount to get the changed account;
*/
OS_ACCOUNT_CHANGED,
/**
* OSAccount associated with the current case has been deleted.
*/
OS_ACCOUNT_REMOVED,
/**
* Hosts associated with the current case added.
*/
HOSTS_ADDED,
/**
* Hosts associated with the current case has changed.
*/
HOSTS_CHANGED,
/**
* Hosts associated with the current case has been deleted.
*/
HOSTS_DELETED,
/**
* Persons associated with the current case added.
*/
PERSONS_ADDED,
/**
* Persons associated with the current case has changed.
*/
PERSONS_CHANGED,
/**
* Persons associated with the current case has been deleted.
*/
PERSONS_DELETED
;
PERSONS_DELETED;
};
/**
@ -507,27 +498,34 @@ public class Case {
}
@Subscribe
public void publishOsAccountAddedEvent(OsAccountsCreationEvent event) {
public void publishOsAccountAddedEvent(TskEvent.OsAccountsAddedTskEvent event) {
for(OsAccount account: event.getOsAcounts()) {
eventPublisher.publish(new OsAccountAddedEvent(account));
}
}
@Subscribe
public void publishOsAccountChangedEvent(OsAccountsUpdateEvent event) {
public void publishOsAccountChangedEvent(TskEvent.OsAccountsChangedTskEvent event) {
for(OsAccount account: event.getOsAcounts()) {
eventPublisher.publish(new OsAccountChangedEvent(account));
}
}
@Subscribe
public void publishOsAccountDeletedEvent(TskEvent.OsAccountsDeletedTskEvent event) {
for(Long accountId: event.getOsAcountObjectIds()) {
eventPublisher.publish(new OsAccountDeletedEvent(accountId));
}
}
/**
* Publishes an autopsy event from the sleuthkit HostCreationEvent
* Publishes an autopsy event from the sleuthkit HostAddedEvent
* indicating that hosts have been created.
*
* @param event The sleuthkit event for the creation of hosts.
*/
@Subscribe
public void publishHostsAddedEvent(HostsCreationEvent event) {
public void publishHostsAddedEvent(TskEvent.HostsAddedTskEvent event) {
eventPublisher.publish(new HostsAddedEvent(
event == null ? Collections.emptyList() : event.getHosts()));
}
@ -539,7 +537,7 @@ public class Case {
* @param event The sleuthkit event for the updating of hosts.
*/
@Subscribe
public void publishHostsChangedEvent(HostsUpdateEvent event) {
public void publishHostsChangedEvent(TskEvent.HostsChangedTskEvent event) {
eventPublisher.publish(new HostsChangedEvent(
event == null ? Collections.emptyList() : event.getHosts()));
}
@ -551,31 +549,31 @@ public class Case {
* @param event The sleuthkit event for the deleting of hosts.
*/
@Subscribe
public void publishHostsDeletedEvent(HostsDeletionEvent event) {
public void publishHostsDeletedEvent(TskEvent.HostsDeletedTskEvent event) {
eventPublisher.publish(new HostsRemovedEvent(
event == null ? Collections.emptyList() : event.getHosts()));
}
/**
* Publishes an autopsy event from the sleuthkit PersonCreationEvent
* Publishes an autopsy event from the sleuthkit PersonAddedEvent
* indicating that persons have been created.
*
* @param event The sleuthkit event for the creation of persons.
*/
@Subscribe
public void publishPersonsAddedEvent(PersonsCreationEvent event) {
public void publishPersonsAddedEvent(TskEvent.PersonsAddedTskEvent event) {
eventPublisher.publish(new PersonsAddedEvent(
event == null ? Collections.emptyList() : event.getPersons()));
}
/**
* Publishes an autopsy event from the sleuthkit PersonUpdateEvent
* Publishes an autopsy event from the sleuthkit PersonChangedEvent
* indicating that persons have been updated.
*
* @param event The sleuthkit event for the updating of persons.
*/
@Subscribe
public void publishPersonsChangedEvent(PersonsUpdateEvent event) {
public void publishPersonsChangedEvent(TskEvent.PersonsChangedTskEvent event) {
eventPublisher.publish(new PersonsChangedEvent(
event == null ? Collections.emptyList() : event.getPersons()));
}
@ -587,8 +585,8 @@ public class Case {
* @param event The sleuthkit event for the deleting of persons.
*/
@Subscribe
public void publishPersonsDeletedEvent(PersonsDeletionEvent event) {
eventPublisher.publish(new PersonsRemovedEvent(
public void publishPersonsDeletedEvent(TskEvent.PersonsDeletedTskEvent event) {
eventPublisher.publish(new PersonsDeletedEvent(
event == null ? Collections.emptyList() : event.getPersons()));
}
}
@ -869,12 +867,12 @@ public class Case {
eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), closedCase, null));
logger.log(Level.INFO, "Closing current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
closedCase.doCloseCaseAction();
currentCase = null;
logger.log(Level.INFO, "Closed current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
} catch (CaseActionException ex) {
logger.log(Level.SEVERE, String.format("Error closing current case %s (%s) in %s", closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()), ex); //NON-NLS
throw ex;
} finally {
currentCase = null;
if (RuntimeProperties.runningWithGUI()) {
updateGUIForCaseClosed();
}
@ -1789,8 +1787,13 @@ public class Case {
eventPublisher.publish(new OsAccountChangedEvent(account));
}
public void notifyOsAccountRemoved(Long osAccountObjectId) {
eventPublisher.publish(new OsAccountDeletedEvent(osAccountObjectId));
}
/**
* Notify via an autopsy event that a host has been added.
*
* @param host The host that has been added.
*/
public void notifyHostAdded(Host host) {
@ -1799,6 +1802,7 @@ public class Case {
/**
* Notify via an autopsy event that a host has been changed.
*
* @param newValue The host that has been updated.
*/
public void notifyHostChanged(Host newValue) {
@ -1807,6 +1811,7 @@ public class Case {
/**
* Notify via an autopsy event that a host has been deleted.
*
* @param host The host that has been deleted.
*/
public void notifyHostDeleted(Host host) {
@ -1815,6 +1820,7 @@ public class Case {
/**
* Notify via an autopsy event that a person has been added.
*
* @param person The person that has been added.
*/
public void notifyPersonAdded(Person person) {
@ -1823,6 +1829,7 @@ public class Case {
/**
* Notify via an autopsy event that a person has been changed.
*
* @param newValue The person that has been updated.
*/
public void notifyPersonChanged(Person newValue) {
@ -1831,10 +1838,11 @@ public class Case {
/**
* Notify via an autopsy event that a person has been deleted.
*
* @param person The person that has been deleted.
*/
public void notifyPersonDeleted(Person person) {
eventPublisher.publish(new PersonsRemovedEvent(Collections.singletonList(person)));
eventPublisher.publish(new PersonsDeletedEvent(Collections.singletonList(person)));
}
/**
@ -1915,7 +1923,7 @@ public class Case {
*
* @return A CaseMetaData object.
*/
CaseMetadata getMetadata() {
public CaseMetadata getMetadata() {
return metadata;
}

View File

@ -218,7 +218,7 @@ public final class CaseMetadata {
*
* @return The path to the metadata file
*/
Path getFilePath() {
public Path getFilePath() {
return metadataFilePath;
}

View File

@ -284,10 +284,10 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
ingestStream = IngestManager.getInstance().openIngestStream(image, settings);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error starting ingest modules", ex);
final List<String> errors = new ArrayList<>();
errors.add(ex.getMessage());
callBack.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return;
// There was an error with ingest, but the data source has already been added
// so proceed with the defaultIngestStream. Code in openIngestStream
// should have caused a dialog to popup with the errors.
ingestStream = new DefaultIngestStream();
}
doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, progress, callBack);

View File

@ -55,8 +55,6 @@ class NewCaseWizardPanel2 implements WizardDescriptor.ValidatingPanel<WizardDesc
public NewCaseVisualPanel2 getComponent() {
if (component == null) {
component = new NewCaseVisualPanel2();
} else {
component.refreshCaseDetailsFields();
}
return component;
@ -137,6 +135,7 @@ class NewCaseWizardPanel2 implements WizardDescriptor.ValidatingPanel<WizardDesc
@Override
public void readSettings(WizardDescriptor settings) {
NewCaseVisualPanel2 panel = getComponent();
panel.refreshCaseDetailsFields();
try {
String lastExaminerName = ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, PROP_EXAMINER_NAME);
String lastExaminerPhone = ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, PROP_EXAMINER_PHONE);

View File

@ -58,7 +58,6 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel {
lbPointOfContactPhoneText.setVisible(false);
lbPointOfContactEmailLabel.setVisible(false);
lbPointOfContactEmailText.setVisible(false);
setUpCaseDetailsFields();
}
OptionalCasePropertiesPanel(boolean editCurrentCase) {

View File

@ -37,11 +37,8 @@ class SolrNotConfiguredDialog extends javax.swing.JDialog {
SolrNotConfiguredDialog() {
super((JFrame) WindowManager.getDefault().getMainWindow(), true);
// Center the startup window.
Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
int width = getSize().width;
int height = getSize().height;
setLocation((screenDimension.width - width) / 2, (screenDimension.height - height) / 2);
initComponents();
setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/warning16.png", false));
}

View File

@ -18,12 +18,18 @@
*/
package org.sleuthkit.autopsy.casemodule;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Iterator;
import java.util.logging.Level;
import org.netbeans.spi.sendopts.OptionProcessor;
import javax.swing.SwingUtilities;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.apputils.ResetWindowsAction;
import org.sleuthkit.autopsy.commandlineingest.CommandLineIngestManager;
import org.sleuthkit.autopsy.commandlineingest.CommandLineOpenCaseManager;
import org.sleuthkit.autopsy.commandlineingest.CommandLineOptionProcessor;
@ -61,13 +67,20 @@ public class StartupWindowProvider implements StartupWindowInterface {
return instance;
}
@NbBundle.Messages({
"# {0} - autFilePath",
"StartupWindowProvider.openCase.noFile=Unable to open previously open case because metadata file not found at: {0}",
"# {0} - reOpenFilePath",
"StartupWindowProvider.openCase.deleteOpenFailure=Unable to open or delete file containing path {0} to previously open case. The previous case will not be opened.",
"# {0} - autFilePath",
"StartupWindowProvider.openCase.cantOpen=Unable to open previously open case with metadata file: {0}"})
private void init() {
if (startupWindowToUse == null) {
// first check whether we are running from command line
if (isRunningFromCommandLine()) {
String defaultArg = getDefaultArgument();
if(defaultArg != null) {
if (defaultArg != null) {
new CommandLineOpenCaseManager(defaultArg).start();
return;
} else {
@ -83,37 +96,42 @@ public class StartupWindowProvider implements StartupWindowInterface {
if (RuntimeProperties.runningWithGUI()) {
checkSolr();
}
//discover the registered windows
Collection<? extends StartupWindowInterface> startupWindows
= Lookup.getDefault().lookupAll(StartupWindowInterface.class);
int windowsCount = startupWindows.size();
if (windowsCount == 1) {
switch (windowsCount) {
case 1:
startupWindowToUse = startupWindows.iterator().next();
logger.log(Level.INFO, "Will use the default startup window: " + startupWindowToUse.toString()); //NON-NLS
} else if (windowsCount == 2) {
logger.log(Level.INFO, "Will use the default startup window: {0}", startupWindowToUse.toString()); //NON-NLS
break;
case 2: {
//pick the non default one
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
while (it.hasNext()) {
StartupWindowInterface window = it.next();
if (!org.sleuthkit.autopsy.casemodule.StartupWindow.class.isInstance(window)) {
startupWindowToUse = window;
logger.log(Level.INFO, "Will use the custom startup window: " + startupWindowToUse.toString()); //NON-NLS
logger.log(Level.INFO, "Will use the custom startup window: {0}", startupWindowToUse.toString()); //NON-NLS
break;
}
}
} else {
break;
}
default: {
// select first non-Autopsy start up window
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
while (it.hasNext()) {
StartupWindowInterface window = it.next();
if (!window.getClass().getCanonicalName().startsWith("org.sleuthkit.autopsy")) {
startupWindowToUse = window;
logger.log(Level.INFO, "Will use the custom startup window: " + startupWindowToUse.toString()); //NON-NLS
logger.log(Level.INFO, "Will use the custom startup window: {0}", startupWindowToUse.toString()); //NON-NLS
break;
}
}
break;
}
}
if (startupWindowToUse == null) {
@ -121,6 +139,45 @@ public class StartupWindowProvider implements StartupWindowInterface {
startupWindowToUse = new org.sleuthkit.autopsy.casemodule.StartupWindow();
}
}
File openPreviousCaseFile = new File(ResetWindowsAction.getCaseToReopenFilePath());
if (openPreviousCaseFile.exists()) {
//do actual opening on another thread
new Thread(() -> {
String caseFilePath = "";
String unableToOpenMessage = null;
try {
//avoid readFileToString having ambiguous arguments
Charset encoding = null;
caseFilePath = FileUtils.readFileToString(openPreviousCaseFile, encoding);
if (new File(caseFilePath).exists()) {
FileUtils.forceDelete(openPreviousCaseFile);
//close the startup window as we attempt to open the case
close();
Case.openAsCurrentCase(caseFilePath);
} else {
unableToOpenMessage = Bundle.StartupWindowProvider_openCase_noFile(caseFilePath);
logger.log(Level.WARNING, unableToOpenMessage);
}
} catch (IOException ex) {
unableToOpenMessage = Bundle.StartupWindowProvider_openCase_deleteOpenFailure(ResetWindowsAction.getCaseToReopenFilePath());
logger.log(Level.WARNING, unableToOpenMessage, ex);
} catch (CaseActionException ex) {
unableToOpenMessage = Bundle.StartupWindowProvider_openCase_cantOpen(caseFilePath);
logger.log(Level.WARNING, unableToOpenMessage, ex);
}
if (RuntimeProperties.runningWithGUI() && !StringUtils.isBlank(unableToOpenMessage)) {
final String message = unableToOpenMessage;
SwingUtilities.invokeLater(() -> {
MessageNotifyUtil.Message.warn(message);
//the case was not opened restore the startup window
open();
});
}
}).start();
}
}
private void checkSolr() {
@ -149,7 +206,7 @@ public class StartupWindowProvider implements StartupWindowInterface {
private boolean isRunningFromCommandLine() {
CommandLineOptionProcessor processor = Lookup.getDefault().lookup(CommandLineOptionProcessor.class);
if(processor != null) {
if (processor != null) {
return processor.isRunFromCommandLine();
}
return false;
@ -162,7 +219,7 @@ public class StartupWindowProvider implements StartupWindowInterface {
*/
private String getDefaultArgument() {
CommandLineOptionProcessor processor = Lookup.getDefault().lookup(CommandLineOptionProcessor.class);
if(processor != null) {
if (processor != null) {
return processor.getDefaultArgument();
}
return null;

View File

@ -33,6 +33,8 @@ import org.sleuthkit.datamodel.TskCoreException;
*/
public class HostsEvent extends TskDataModelChangeEvent<Host> {
private static final long serialVersionUID = 1L;
/**
* Retrieves a list of ids from a list of hosts.
*
@ -42,7 +44,7 @@ public class HostsEvent extends TskDataModelChangeEvent<Host> {
private static List<Long> getIds(List<Host> hosts) {
return getSafeList(hosts).stream()
.filter(h -> h != null)
.map(h -> h.getId()).collect(Collectors.toList());
.map(h -> h.getHostId()).collect(Collectors.toList());
}
/**
@ -76,7 +78,7 @@ public class HostsEvent extends TskDataModelChangeEvent<Host> {
continue;
}
Optional<Host> thisHostOpt = hostManager.getHost(id);
Optional<Host> thisHostOpt = hostManager.getHostById(id);
thisHostOpt.ifPresent((h) -> toRet.add(h));
}
}

View File

@ -0,0 +1,37 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule.events;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.events.AutopsyEvent;
/**
* Event published when an OsAccount is deleted.
*
* oldValue will contain the objectId of the account that was removed. newValue
* will be null.
*/
public final class OsAccountDeletedEvent extends AutopsyEvent {
private static final long serialVersionUID = 1L;
public OsAccountDeletedEvent(Long osAccountObjectId) {
super(Case.Events.OS_ACCOUNT_REMOVED.toString(), osAccountObjectId, null);
}
}

View File

@ -56,7 +56,7 @@ class OsAccountEvent extends TskDataModelChangeEvent<OsAccount> {
@Override
protected List<OsAccount> getDataModelObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
Long id = ids.get(0);
OsAccount account = caseDb.getOsAccountManager().getOsAccount(id);
OsAccount account = caseDb.getOsAccountManager().getOsAccountByObjectId(id);
List<OsAccount> accounts = new ArrayList<>();
accounts.add(account);
return accounts;

View File

@ -25,7 +25,7 @@ import org.sleuthkit.datamodel.Person;
/**
* Event fired when persons are removed.
*/
public class PersonsRemovedEvent extends PersonsEvent {
public class PersonsDeletedEvent extends PersonsEvent {
private static final long serialVersionUID = 1L;
@ -33,7 +33,7 @@ public class PersonsRemovedEvent extends PersonsEvent {
* Main constructor.
* @param dataModelObjects The list of persons that have been deleted.
*/
public PersonsRemovedEvent(List<Person> dataModelObjects) {
public PersonsDeletedEvent(List<Person> dataModelObjects) {
super(Case.Events.PERSONS_DELETED.name(), dataModelObjects);
}
}

View File

@ -42,7 +42,7 @@ public class PersonsEvent extends TskDataModelChangeEvent<Person> {
private static List<Long> getIds(List<Person> persons) {
return getSafeList(persons).stream()
.filter(h -> h != null)
.map(h -> h.getId()).collect(Collectors.toList());
.map(h -> h.getPersonId()).collect(Collectors.toList());
}
/**

View File

@ -51,7 +51,7 @@ import org.sleuthkit.datamodel.TskException;
* View correlation results from other cases
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
@ServiceProvider(service = DataContentViewer.class, position = 9)
@ServiceProvider(service = DataContentViewer.class, position = 10)
@Messages({"DataContentViewerOtherCases.title=Other Occurrences",
"DataContentViewerOtherCases.toolTip=Displays instances of the selected file/artifact from other occurrences."})
public final class DataContentViewerOtherCases extends JPanel implements DataContentViewer {

View File

@ -511,8 +511,6 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
* from the current case with matching MD5 hashes.
*
* @param corAttr CorrelationAttribute to query for
* @param dataSourceName Data source to filter results
* @param deviceId Device Id to filter results
*
* @return A collection of correlated artifact instances
*/
@ -618,6 +616,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
*
* @param corAttr The CorrelationAttribute containing the MD5 to search for
* @param openCase The current case
* @param file The current file.
*
* @return List of matching AbstractFile objects
*

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.casemodule.Case;
@ -860,10 +861,10 @@ public interface CentralRepository {
* Get account type by type name.
*
* @param accountTypeName account type name to look for
* @return CR account type
* @return CR account type (if found)
* @throws CentralRepoException
*/
CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException;
Optional<CentralRepoAccountType> getAccountTypeByName(String accountTypeName) throws CentralRepoException;
/**
* Gets all account types.

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import org.openide.util.NbBundle.Messages;
@ -308,7 +309,11 @@ public class CorrelationAttributeUtil {
if (Account.Type.DEVICE.getTypeName().equalsIgnoreCase(accountTypeStr) == false && predefinedAccountType != null) {
// Get the corresponding CentralRepoAccountType from the database.
CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
Optional<CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
if (!optCrAccountType.isPresent()) {
return;
}
CentralRepoAccountType crAccountType = optCrAccountType.get();
int corrTypeId = crAccountType.getCorrelationTypeId();
CorrelationAttributeInstance.Type corrType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);

View File

@ -26,8 +26,10 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.TskCoreException;
/**
* This class represents an association between a Persona and an Account.
@ -206,10 +208,15 @@ public class PersonaAccount {
);
// create account
CentralRepoAccount.CentralRepoAccountType crAccountType = getCRInstance().getAccountTypeByName(rs.getString("type_name"));
String accountTypeName = rs.getString("type_name");
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = getCRInstance().getAccountTypeByName(accountTypeName);
if (! optCrAccountType.isPresent()) {
// The CR account can not be null, so throw an exception
throw new CentralRepoException("Account type with name '" + accountTypeName + "' not found in Central Repository");
}
CentralRepoAccount account = new CentralRepoAccount(
rs.getInt("account_id"),
crAccountType,
optCrAccountType.get(),
rs.getString("account_unique_identifier"));
// create persona account
@ -389,10 +396,15 @@ public class PersonaAccount {
while (rs.next()) {
// create account
CentralRepoAccount.CentralRepoAccountType crAccountType = getCRInstance().getAccountTypeByName(rs.getString("type_name"));
String accountTypeName = rs.getString("type_name");
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = getCRInstance().getAccountTypeByName(accountTypeName);
if (! optCrAccountType.isPresent()) {
// The CR account can not be null, so throw an exception
throw new CentralRepoException("Account type with name '" + accountTypeName + "' not found in Central Repository");
}
CentralRepoAccount account = new CentralRepoAccount(
rs.getInt("account_id"),
crAccountType,
optCrAccountType.get(),
rs.getString("account_unique_identifier"));
accountsList.add(account);

View File

@ -37,6 +37,7 @@ import java.time.LocalDate;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@ -78,7 +79,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
private static final int CASE_CACHE_TIMEOUT = 5;
private static final int DATA_SOURCE_CACHE_TIMEOUT = 5;
private static final int ACCOUNTS_CACHE_TIMEOUT = 5;
private static final Cache<String, CentralRepoAccountType> accountTypesCache = CacheBuilder.newBuilder().build();
private static final Cache<String, Optional<CentralRepoAccountType>> accountTypesCache = CacheBuilder.newBuilder().build();
private static final Cache<Pair<CentralRepoAccountType, String>, CentralRepoAccount> accountsCache = CacheBuilder.newBuilder()
.expireAfterWrite(ACCOUNTS_CACHE_TIMEOUT, TimeUnit.MINUTES).
build();
@ -1115,7 +1116,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
}
@Override
public CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException {
public Optional<CentralRepoAccountType> getAccountTypeByName(String accountTypeName) throws CentralRepoException {
try {
return accountTypesCache.get(accountTypeName, () -> getCRAccountTypeFromDb(accountTypeName));
} catch (CacheLoader.InvalidCacheLoadException | ExecutionException ex) {
@ -1155,7 +1156,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
*
* @throws CentralRepoException
*/
private CentralRepoAccountType getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException {
private Optional<CentralRepoAccountType> getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException {
String sql = "SELECT * FROM account_types WHERE type_name = ?";
try (Connection conn = connect();
@ -1166,10 +1167,11 @@ abstract class RdbmsCentralRepo implements CentralRepository {
if (resultSet.next()) {
Account.Type acctType = new Account.Type(accountTypeName, resultSet.getString("display_name"));
CentralRepoAccountType crAccountType = new CentralRepoAccountType(resultSet.getInt("id"), acctType, resultSet.getInt("correlation_type_id"));
accountTypesCache.put(accountTypeName, crAccountType);
return crAccountType;
accountTypesCache.put(accountTypeName, Optional.of(crAccountType));
return Optional.of(crAccountType);
} else {
throw new CentralRepoException("Failed to find entry for account type = " + accountTypeName);
accountTypesCache.put(accountTypeName, Optional.empty());
return Optional.empty();
}
}
} catch (SQLException ex) {

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.communications.relationships;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import org.openide.nodes.AbstractNode;
@ -75,9 +76,9 @@ final class CorrelationCaseChildNodeFactory extends ChildFactory<CorrelationCase
accounts.forEach((account) -> {
try {
CorrelationAttributeInstance.Type correlationType = getCorrelationType(account.getAccountType());
if (correlationType != null) {
List<CorrelationAttributeInstance> correlationInstances = dbInstance.getArtifactInstancesByTypeValue(correlationType, account.getTypeSpecificID());
Optional<CorrelationAttributeInstance.Type> optCorrelationType = getCorrelationType(account.getAccountType());
if (optCorrelationType.isPresent()) {
List<CorrelationAttributeInstance> correlationInstances = dbInstance.getArtifactInstancesByTypeValue(optCorrelationType.get(), account.getTypeSpecificID());
correlationInstances.forEach((correlationInstance) -> {
CorrelationCase correlationCase = correlationInstance.getCorrelationCase();
uniqueCaseMap.put(correlationCase.getCaseUUID(), correlationCase);
@ -103,20 +104,22 @@ final class CorrelationCaseChildNodeFactory extends ChildFactory<CorrelationCase
*
* @param accountType Account type
*
* @return CorrelationAttributeInstance.Type for given account or null if
* @return CorrelationAttributeInstance.Type for given account or empty if
* there is no match
*
* @throws CentralRepoException
*/
private CorrelationAttributeInstance.Type getCorrelationType(Account.Type accountType) throws CentralRepoException {
private Optional<CorrelationAttributeInstance.Type> getCorrelationType(Account.Type accountType) throws CentralRepoException {
String accountTypeStr = accountType.getTypeName();
if (Account.Type.DEVICE.getTypeName().equalsIgnoreCase(accountTypeStr) == false) {
CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
int corrTypeId = crAccountType.getCorrelationTypeId();
return CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
if (optCrAccountType.isPresent()) {
int corrTypeId = optCrAccountType.get().getCorrelationTypeId();
return Optional.of(CentralRepository.getInstance().getCorrelationTypeById(corrTypeId));
}
return null;
}
return Optional.empty();
}
/**

View File

@ -21,10 +21,12 @@ package org.sleuthkit.autopsy.communications.relationships;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
import javax.swing.SwingWorker;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
@ -61,9 +63,11 @@ class SummaryPanelWorker extends SwingWorker<SummaryPanelWorker.SummaryWorkerRes
CentralRepoAccount crAccount = null;
List<String> stringList = new ArrayList<>();
List<AccountFileInstance> accountFileInstanceList = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().getAccountFileInstances(account);
if (accountFileInstanceList != null) {
for (AccountFileInstance instance : accountFileInstanceList) {
stringList.add(instance.getFile().getUniquePath());
}
}
List<Persona> personaList = new ArrayList<>();
if (CentralRepository.isEnabled()) {
@ -74,14 +78,17 @@ class SummaryPanelWorker extends SwingWorker<SummaryPanelWorker.SummaryWorkerRes
personaList.add(pAccount.getPersona());
}
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName());
if (optCrAccountType.isPresent()) {
try {
crAccount = CentralRepository.getInstance().getAccount(CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName()), account.getTypeSpecificID());
crAccount = CentralRepository.getInstance().getAccount(optCrAccountType.get(), account.getTypeSpecificID());
} catch (InvalidAccountIDException unused) {
// This was probably caused to a phone number not making
// threw the normalization.
logger.log(Level.WARNING, String.format("Exception thrown from CR getAccount for account %s (%d)", account.getTypeSpecificID(), account.getAccountID()));
}
}
}
return new SummaryWorkerResults(stringList, personaList, crAccount);
}

View File

@ -216,9 +216,11 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
List<String> fileRefList = results.getPaths();
if (fileRefList != null) {
fileRefList.forEach(value -> {
fileRefListModel.addElement(value);
});
}
CardLayout cardLayout = (CardLayout) fileRefPane.getLayout();
cardLayout.show(fileRefPane, "listPanelCard");

View File

@ -61,7 +61,7 @@ import org.jsoup.nodes.Element;
* Annotations view of file contents.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
@ServiceProvider(service = DataContentViewer.class, position = 8)
@ServiceProvider(service = DataContentViewer.class, position = 9)
@Messages({
"AnnotationsContentViewer.title=Annotations",
"AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content.",

View File

@ -32,6 +32,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
@ -630,13 +631,15 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
// make a list of all unique accounts for this contact
if (!account.getAccountType().equals(Account.Type.DEVICE)) {
CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName());
CentralRepoAccount crAccount = CentralRepository.getInstance().getAccount(crAccountType, account.getTypeSpecificID());
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName());
if (optCrAccountType.isPresent()) {
CentralRepoAccount crAccount = CentralRepository.getInstance().getAccount(optCrAccountType.get(), account.getTypeSpecificID());
if (crAccount != null && uniqueAccountsList.contains(crAccount) == false) {
uniqueAccountsList.add(crAccount);
}
}
}
Collection<PersonaAccount> personaAccounts = PersonaAccount.getPersonaAccountsForAccount(account);
if (personaAccounts != null && !personaAccounts.isEmpty()) {

View File

@ -48,7 +48,7 @@ import org.sleuthkit.datamodel.TskCoreException;
* usage, if known.
*
*/
@ServiceProvider(service = DataContentViewer.class, position = 7)
@ServiceProvider(service = DataContentViewer.class, position = 8)
public final class ContextViewer extends javax.swing.JPanel implements DataContentViewer {
private static final long serialVersionUID = 1L;

View File

@ -1,3 +1,4 @@
OsAccountDataPanel_administrator_title=Administrator
OsAccountDataPanel_basic_address=Address
OsAccountDataPanel_basic_admin=Administrator
OsAccountDataPanel_basic_creationDate=Creation Date
@ -5,12 +6,16 @@ OsAccountDataPanel_basic_fullname=Full Name
OsAccountDataPanel_basic_login=Login
OsAccountDataPanel_basic_title=Basic Properties
OsAccountDataPanel_basic_type=Type
OsAccountDataPanel_data_accessed_title=Last Login
OsAccountDataPanel_host_count_title=Login Count
# {0} - hostName
OsAccountDataPanel_host_section_title={0} Details
OsAccountDataPanel_realm_address=Address
OsAccountDataPanel_realm_confidence=Confidence
OsAccountDataPanel_realm_name=Name
OsAccountDataPanel_realm_scope=Scope
OsAccountDataPanel_realm_title=Realm Properties
OsAccountDataPanel_realm_unknown=Unknown
OsAccountViewer_title=Os Account
OsAccountViewer_tooltip=Viewer for OS accounts related to the selected node.
OsAccountViewer_title=OS Account
OsAccountViewer_tooltip=Viewer for Operating System accounts related to the selected node.
TableDataPanel.titleLabel.text=Title

View File

@ -41,13 +41,15 @@ import javax.swing.SwingWorker;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.contentviewers.osaccount.SectionData.RowData;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.OsAccountAttribute;
import org.sleuthkit.datamodel.OsAccount.OsAccountAttribute;
import org.sleuthkit.datamodel.OsAccountInstance;
import org.sleuthkit.datamodel.OsAccountManager;
import org.sleuthkit.datamodel.OsAccountRealm;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* Panel for displaying the properties of an OsAccount.
@ -82,6 +84,23 @@ public class OsAccountDataPanel extends JPanel {
* @param account OsAccount to display, if null is passed the panel will
* appear blank.
*/
void setOsAccountId(Long osAccountId) {
removeAll();
revalidate();
if (osAccountId != null) {
setLayout(new BorderLayout());
add(new JLabel("Loading OsAccount Data..."), BorderLayout.NORTH);
if (dataFetcher != null && !dataFetcher.isDone()) {
dataFetcher.cancel(true);
}
dataFetcher = new PanelDataFetcher(osAccountId);
dataFetcher.execute();
}
}
void setOsAccount(OsAccount account) {
removeAll();
revalidate();
@ -161,7 +180,9 @@ public class OsAccountDataPanel extends JPanel {
data.addData(Bundle.OsAccountDataPanel_basic_address(),
account.getName() == null || account.getName().isEmpty() ? "" : account.getName());
data.addData(Bundle.OsAccountDataPanel_basic_type(), account.getOsAccountType().getName());
data.addData(Bundle.OsAccountDataPanel_basic_type(),
account.getOsAccountType().isPresent() ? account.getOsAccountType().get().getName() : "");
Optional<Long> crTime = account.getCreationTime();
if (crTime.isPresent()) {
@ -191,11 +212,10 @@ public class OsAccountDataPanel extends JPanel {
private SectionData buildRealmProperties(OsAccountRealm realm) {
SectionData data = new SectionData(Bundle.OsAccountDataPanel_realm_title());
Optional<String> optional = realm.getRealmName();
data.addData(Bundle.OsAccountDataPanel_realm_name(),
optional.isPresent() ? optional.get() : Bundle.OsAccountDataPanel_realm_unknown());
String realmName = realm.getRealmNames().isEmpty() ? Bundle.OsAccountDataPanel_realm_unknown() : realm.getRealmNames().get(0);
data.addData(Bundle.OsAccountDataPanel_realm_name(), realmName);
optional = realm.getRealmAddr();
Optional<String> optional = realm.getRealmAddr();
data.addData(Bundle.OsAccountDataPanel_realm_address(),
optional.isPresent() ? optional.get() : "");
@ -208,10 +228,33 @@ public class OsAccountDataPanel extends JPanel {
return data;
}
@Messages({
"# {0} - hostName",
"OsAccountDataPanel_host_section_title={0} Details",
"OsAccountDataPanel_host_count_title=Login Count",
"OsAccountDataPanel_data_accessed_title=Last Login",
"OsAccountDataPanel_administrator_title=Administrator"
})
private SectionData buildHostData(Host host, List<OsAccountAttribute> attributeList) {
SectionData data = new SectionData(host.getName());
SectionData data = new SectionData(Bundle.OsAccountDataPanel_host_section_title(host.getName()));
for (OsAccountAttribute attribute : attributeList) {
data.addData(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
String displayName = attribute.getAttributeType().getDisplayName();
String value = attribute.getDisplayString();
if(attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT.getTypeID()) {
displayName = Bundle.OsAccountDataPanel_host_count_title();
} else if(attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IS_ADMIN.getTypeID()) {
displayName = Bundle.OsAccountDataPanel_administrator_title();
if(attribute.getValueInt() == 0) {
value = "False";
} else {
value = "True";
}
} else if(attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID()) {
displayName = Bundle.OsAccountDataPanel_data_accessed_title();
}
data.addData(displayName, value);
}
return data;
@ -237,7 +280,7 @@ public class OsAccountDataPanel extends JPanel {
* @param row The row in the layout.
*/
private void addPropertyName(String key, int row) {
JLabel label = new JLabel(key);
JLabel label = new JLabel(key + ":");
add(label, getPropertyNameContraints(row));
}
@ -320,24 +363,39 @@ public class OsAccountDataPanel extends JPanel {
*/
private class PanelDataFetcher extends SwingWorker<WorkerResults, Void> {
private final OsAccount account;
private final Long accountId;
private OsAccount account;
/**
* Construct a new worker for the given account.
*
* @param account
*/
PanelDataFetcher(Long accountId) {
this.accountId = accountId;
this.account = null;
}
PanelDataFetcher(OsAccount account) {
this.account = account;
this.accountId = null;
}
@Override
protected WorkerResults doInBackground() throws Exception {
Map<Host, List<OsAccountAttribute>> hostMap = new HashMap<>();
Map<Host, DataSource> instanceMap = new HashMap<>();
OsAccountManager osAccountManager = Case.getCurrentCase().getSleuthkitCase().getOsAccountManager();
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
OsAccountManager osAccountManager = skCase.getOsAccountManager();
if(account == null) {
account = osAccountManager.getOsAccountByObjectId(accountId);
}
OsAccountRealm realm = skCase.getOsAccountRealmManager().getRealmByRealmId(account.getRealmId());
List<Host> hosts = osAccountManager.getHosts(account);
List<OsAccountAttribute> attributeList = account.getOsAccountAttributes();
List<OsAccountAttribute> attributeList = account.getExtendedOsAccountAttributes();
if (attributeList != null) {
if (hosts != null) {
@ -363,7 +421,7 @@ public class OsAccountDataPanel extends JPanel {
// Add attribute lists to the hostMap
for (Host host : hosts) {
List<OsAccountAttribute> atList = idMap.get(host.getId());
List<OsAccountAttribute> atList = idMap.get(host.getHostId());
if (atList != null) {
hostMap.put(host, atList);
}
@ -385,7 +443,7 @@ public class OsAccountDataPanel extends JPanel {
}
}
return new WorkerResults(hostMap, instanceMap);
return new WorkerResults(hostMap, instanceMap, realm);
}
@Override
@ -413,18 +471,20 @@ public class OsAccountDataPanel extends JPanel {
hostDataMap.forEach((K, V) -> data.add(buildHostData(K, V)));
}
OsAccountRealm realm = account.getRealm();
OsAccountRealm realm = results.getRealm();
if (realm != null) {
data.add(buildRealmProperties(realm));
}
Map<Host, DataSource> instanceMap = results.getDataSourceMap();
if (!instanceMap.isEmpty()) {
SectionData instanceSection = new SectionData("Instances");
instanceMap.forEach((K, V) -> instanceSection.addData(K.getName(), V.getName()));
data.add(instanceSection);
}
// Removing the instance section for now. Leaving code here for
// future use.
// Map<Host, DataSource> instanceMap = results.getDataSourceMap();
// if (!instanceMap.isEmpty()) {
// SectionData instanceSection = new SectionData("Instances");
// instanceMap.forEach((K, V) -> instanceSection.addData(K.getName(), V.getName()));
//
// data.add(instanceSection);
// }
addDataComponents(data);
@ -442,6 +502,7 @@ public class OsAccountDataPanel extends JPanel {
private final Map<Host, List<OsAccountAttribute>> attributeMap;
private final Map<Host, DataSource> instanceMap;
private final OsAccountRealm realm;
/**
* Construct a new WorkerResult object.
@ -451,9 +512,10 @@ public class OsAccountDataPanel extends JPanel {
* @param instanceMap A map of data to display OsAccount instance
* information.
*/
WorkerResults(Map<Host, List<OsAccountAttribute>> attributeMap, Map<Host, DataSource> instanceMap) {
WorkerResults(Map<Host, List<OsAccountAttribute>> attributeMap, Map<Host, DataSource> instanceMap, OsAccountRealm realm) {
this.attributeMap = attributeMap;
this.instanceMap = instanceMap;
this.realm = realm;
}
/**
@ -475,5 +537,9 @@ public class OsAccountDataPanel extends JPanel {
Map<Host, DataSource> getDataSourceMap() {
return instanceMap;
}
OsAccountRealm getRealm() {
return realm;
}
}
}

View File

@ -11,14 +11,20 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="mainScrollPane">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[200, 0]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>

View File

@ -18,7 +18,10 @@
*/
package org.sleuthkit.autopsy.contentviewers.osaccount;
import java.awt.BorderLayout;
import java.awt.Component;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Optional;
import java.util.logging.Level;
import org.openide.nodes.Node;
@ -34,7 +37,7 @@ import org.sleuthkit.datamodel.TskCoreException;
/**
* DataContentViewer for OsAccounts.
*/
@ServiceProvider(service = DataContentViewer.class, position = 12)
@ServiceProvider(service = DataContentViewer.class, position = 7)
public class OsAccountViewer extends javax.swing.JPanel implements DataContentViewer {
private static final long serialVersionUID = 1L;
@ -53,26 +56,29 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
@Override
public void setNode(Node node) {
OsAccount osAccount = null;
Long osAccountId = null;
try {
osAccount = node.getLookup().lookup(OsAccount.class);
if (osAccount == null) {
Optional<OsAccount> optional;
OsAccount osAccount = node.getLookup().lookup(OsAccount.class);
if (osAccount != null) {
dataPanel.setOsAccount(osAccount);
return;
}
Optional<Long> optional;
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
if (file != null) {
optional = file.getOsAccount();
optional = file.getOsAccountObjectId();
if (optional.isPresent()) {
osAccount = optional.get();
}
osAccountId = optional.get();
}
}
if (osAccount == null) {
if (osAccountId == null) {
DataArtifact dataArtifact = node.getLookup().lookup(DataArtifact.class);
if (dataArtifact != null) {
Optional<OsAccount> optional = dataArtifact.getOsAccount();
optional = dataArtifact.getOsAccountObjectId();
if (optional.isPresent()) {
osAccount = optional.get();
osAccountId = optional.get();
}
}
@ -81,13 +87,13 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
logger.log(Level.SEVERE, String.format("Failed to get OsAccount for node %s", node.getDisplayName()), ex);
}
if (osAccount != null) {
dataPanel.setOsAccount(osAccount);
if (osAccountId != null) {
dataPanel.setOsAccountId(osAccountId);
}
}
@Messages({
"OsAccountViewer_title=Os Account"
"OsAccountViewer_title=OS Account"
})
@Override
public String getTitle() {
@ -95,7 +101,7 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
}
@Messages({
"OsAccountViewer_tooltip=Viewer for OS accounts related to the selected node."
"OsAccountViewer_tooltip=Viewer for Operating System accounts related to the selected node."
})
@Override
public String getToolTip() {
@ -125,8 +131,8 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
try {
return osAccount != null
|| (file != null && file.getOsAccount().isPresent())
|| (dataArtifact != null && dataArtifact.getOsAccount().isPresent());
|| (file != null && file.getOsAccountObjectId().isPresent())
|| (dataArtifact != null && dataArtifact.getOsAccountObjectId().isPresent());
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Failed to determine if node %s is Supported for OsAccountViewer", node.getDisplayName()), ex);
return false;
@ -150,8 +156,14 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
mainScrollPane = new javax.swing.JScrollPane();
setLayout(new java.awt.BorderLayout());
add(mainScrollPane, java.awt.BorderLayout.CENTER);
setLayout(new java.awt.GridBagLayout());
mainScrollPane.setPreferredSize(new java.awt.Dimension(200, 0));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
add(mainScrollPane, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents

View File

@ -51,10 +51,10 @@ final class SectionData implements Iterable<SectionData.RowData<String, String>>
}
/**
* Add a new property name\property value pair.
* Add a new property name/property value pair.
*
* @param key The property display name.
* @param value The property value.
* @param properytName The property display name.
* @param propertyValue The property value.
*/
void addData(String properytName, String propertyValue) {
data.add(new RowData<>(properytName, propertyValue));

View File

@ -244,7 +244,7 @@
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="23" max="-2" attributes="0"/>
<Component id="memFieldValidationLabel" min="-2" pref="478" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<EmptySpace pref="12" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
@ -255,7 +255,24 @@
</Group>
</Group>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="restartNecessaryWarning" alignment="0" min="-2" pref="615" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="heapFileLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="heapFieldValidationLabel" min="-2" pref="478" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<Component id="heapDumpFileField" min="-2" pref="415" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="heapDumpBrowseButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
@ -293,7 +310,15 @@
<Component id="maxLogFileCount" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="logFileCount" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="heapFileLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="heapDumpFileField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="heapDumpBrowseButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="heapFieldValidationLabel" min="-2" pref="16" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="restartNecessaryWarning" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
@ -414,6 +439,40 @@
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="heapFileLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapFileLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="heapDumpFileField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapDumpFileField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="heapDumpBrowseButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapDumpBrowseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="heapDumpBrowseButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="heapFieldValidationLabel">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapFieldValidationLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="tempDirectoryPanel">
@ -462,7 +521,7 @@
</Group>
</Group>
</Group>
<EmptySpace pref="158" max="32767" attributes="0"/>
<EmptySpace pref="164" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>

View File

@ -29,8 +29,11 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
@ -38,7 +41,9 @@ import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tools.ant.types.Commandline;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
@ -76,6 +81,7 @@ import org.sleuthkit.autopsy.report.ReportBranding;
final class AutopsyOptionsPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private static final String DEFAULT_HEAP_DUMP_FILE_FIELD = "";
private final JFileChooser logoFileChooser;
private final JFileChooser tempDirChooser;
private static final String ETC_FOLDER_NAME = "etc";
@ -88,6 +94,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private String initialMemValue = Long.toString(Runtime.getRuntime().maxMemory() / ONE_BILLION);
private final ReportBranding reportBranding;
private final JFileChooser heapFileChooser;
/**
* Instantiate the Autopsy options panel.
@ -104,12 +111,18 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
tempDirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
tempDirChooser.setMultiSelectionEnabled(false);
if (!PlatformUtil.is64BitJVM() || Version.getBuildType() == Version.Type.DEVELOPMENT) {
heapFileChooser = new JFileChooser();
heapFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
heapFileChooser.setMultiSelectionEnabled(false);
if (!isJVMHeapSettingsCapable()) {
//32 bit JVM has a max heap size of 1.4 gb to 4 gb depending on OS
//So disabling the setting of heap size when the JVM is not 64 bit
//Is the safest course of action
//And the file won't exist in the install folder when running through netbeans
memField.setEnabled(false);
heapDumpFileField.setEnabled(false);
heapDumpBrowseButton.setEnabled(false);
solrMaxHeapSpinner.setEnabled(false);
}
systemMemoryTotal.setText(Long.toString(getSystemMemoryInGB()));
@ -118,14 +131,22 @@ 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 textFieldListener = new TextFieldListener();
agencyLogoPathField.getDocument().addDocumentListener(textFieldListener);
tempCustomField.getDocument().addDocumentListener(new TempCustomTextListener());
agencyLogoPathField.getDocument().addDocumentListener(new TextFieldListener(null));
heapDumpFileField.getDocument().addDocumentListener(new TextFieldListener(this::isHeapPathValid));
tempCustomField.getDocument().addDocumentListener(new TextFieldListener(this::evaluateTempDirState));
logFileCount.setText(String.valueOf(UserPreferences.getLogFileCount()));
reportBranding = new ReportBranding();
}
/**
* Returns whether or not the jvm runtime heap settings can effectively be changed.
* @return Whether or not the jvm runtime heap settings can effectively be changed.
*/
private static boolean isJVMHeapSettingsCapable() {
return PlatformUtil.is64BitJVM() && Version.getBuildType() != Version.Type.DEVELOPMENT;
}
/**
* Get the total system memory in gigabytes which exists on the machine
* which the application is running.
@ -140,18 +161,23 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
/**
* Gets the currently saved max java heap space in gigabytes.
* @param The conf file memory value (i.e. 4G).
*
* @return @throws IOException when unable to get a valid setting
* @return The value in gigabytes.
* @throws IOException when unable to get a valid setting
*/
private long getCurrentJvmMaxMemoryInGB() throws IOException {
String currentXmx = getCurrentXmxValue();
private long getCurrentJvmMaxMemoryInGB(String confFileMemValue) throws IOException {
char units = '-';
Long value = 0L;
if (currentXmx.length() > 1) {
units = currentXmx.charAt(currentXmx.length() - 1);
value = Long.parseLong(currentXmx.substring(0, currentXmx.length() - 1));
if (confFileMemValue.length() > 1) {
units = confFileMemValue.charAt(confFileMemValue.length() - 1);
try {
value = Long.parseLong(confFileMemValue.substring(0, confFileMemValue.length() - 1));
} catch (NumberFormatException ex) {
throw new IOException("Unable to properly parse memory number.", ex);
}
} else {
throw new IOException("No memory setting found in String: " + currentXmx);
throw new IOException("No memory setting found in String: " + confFileMemValue);
}
//some older .conf files might have the units as megabytes instead of gigabytes
switch (units) {
@ -166,33 +192,6 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
}
}
/*
* The value currently saved in the conf file as the max java heap space
* available to this application. Form will be an integer followed by a
* character indicating units. Helper method for
* getCurrentJvmMaxMemoryInGB()
*
* @return the saved value for the max java heap space
*
* @throws IOException if the conf file does not exist in either the user
* directory or the install directory
*/
private String getCurrentXmxValue() throws IOException {
String[] settings;
String currentSetting = "";
File userConfFile = getUserFolderConfFile();
if (!userConfFile.exists()) {
settings = getDefaultsFromFileContents(readConfFile(getInstallFolderConfFile()));
} else {
settings = getDefaultsFromFileContents(readConfFile(userConfFile));
}
for (String option : settings) {
if (option.startsWith("-J-Xmx")) {
currentSetting = option.replace("-J-Xmx", "").trim();
}
}
return currentSetting;
}
/**
* Get the conf file from the install directory which stores the default
@ -230,6 +229,60 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
return new File(userEtcFolder, confFileName);
}
private static final String JVM_SETTINGS_REGEX_PARAM = "options";
private static final String JVM_SETTINGS_REGEX_STR = "^\\s*default_options\\s*=\\s*\"?(?<" + JVM_SETTINGS_REGEX_PARAM + ">.+?)\"?\\s*$";
private static final Pattern JVM_SETTINGS_REGEX = Pattern.compile(JVM_SETTINGS_REGEX_STR);
private static final String XMX_REGEX_PARAM = "mem";
private static final String XMX_REGEX_STR = "^\\s*\\-J\\-Xmx(?<" + XMX_REGEX_PARAM + ">.+?)\\s*$";
private static final Pattern XMX_REGEX = Pattern.compile(XMX_REGEX_STR);
private static final String HEAP_DUMP_REGEX_PARAM = "path";
private static final String HEAP_DUMP_REGEX_STR = "^\\s*\\-J\\-XX:HeapDumpPath=(\\\")?\\s*(?<" + HEAP_DUMP_REGEX_PARAM + ">.+?)\\s*(\\\")?$";
private static final Pattern HEAP_DUMP_REGEX = Pattern.compile(HEAP_DUMP_REGEX_STR);
/**
* Parse the autopsy conf file line. If the line is the default_options line,
* then replaces with current memory and heap path value. Otherwise, returns
* the line provided as parameter.
*
* @param line The line.
* @param memText The text to add as an argument to be used as memory with -J-Xmx.
* @param heapText The text to add as an argument to be used as the heap dump path with
* -J-XX:HeapDumpPath.
* @return The line modified to contain memory and heap arguments.
*/
private static String updateConfLine(String line, String memText, String heapText) {
Matcher match = JVM_SETTINGS_REGEX.matcher(line);
if (match.find()) {
// split on command line arguments
String[] parsedArgs = Commandline.translateCommandline(match.group(JVM_SETTINGS_REGEX_PARAM));
String memString = "-J-Xmx" + memText.replaceAll("[^\\d]", "") + "g";
// only add in heap path argument if a heap path is specified
String heapString = null;
if (StringUtils.isNotBlank(heapText)) {
while (heapText.endsWith("\\") && heapText.length() > 0) {
heapText = heapText.substring(0, heapText.length() - 1);
}
heapString = String.format("-J-XX:HeapDumpPath=\"%s\"", heapText);
}
Stream<String> argsNoMemHeap = Stream.of(parsedArgs)
// remove saved version of memory and heap dump path
.filter(s -> !s.matches(XMX_REGEX_STR) && !s.matches(HEAP_DUMP_REGEX_STR));
String newArgs = Stream.concat(argsNoMemHeap, Stream.of(memString, heapString))
.filter(s -> s != null)
.collect(Collectors.joining(" "));
return String.format("default_options=\"%s\"", newArgs);
};
return line;
}
/**
* Take the conf file in the install directory and save a copy of it to the
* user directory. The copy will be modified to include the current memory
@ -239,25 +292,124 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
* install folders conf file
*/
private void writeEtcConfFile() throws IOException {
StringBuilder content = new StringBuilder();
List<String> confFile = readConfFile(getInstallFolderConfFile());
for (String line : confFile) {
if (line.contains("-J-Xmx")) {
String[] splitLine = line.split(" ");
StringJoiner modifiedLine = new StringJoiner(" ");
for (String piece : splitLine) {
if (piece.contains("-J-Xmx")) {
piece = "-J-Xmx" + memField.getText() + "g";
String fileText = readConfFile(getInstallFolderConfFile()).stream()
.map((line) -> updateConfLine(line, memField.getText(), heapDumpFileField.getText()))
.collect(Collectors.joining("\n"));
FileUtils.writeStringToFile(getUserFolderConfFile(), fileText, "UTF-8");
}
modifiedLine.add(piece);
/**
* Values for configuration located in the /etc/\*.conf file.
*/
private static class ConfValues {
private final String XmxVal;
private final String heapDumpPath;
/**
* Main constructor.
* @param XmxVal The heap memory size.
* @param heapDumpPath The heap dump path.
*/
ConfValues(String XmxVal, String heapDumpPath) {
this.XmxVal = XmxVal;
this.heapDumpPath = heapDumpPath;
}
content.append(modifiedLine.toString());
} else {
content.append(line);
/**
* Returns the heap memory value specified in the conf file.
* @return The heap memory value specified in the conf file.
*/
String getXmxVal() {
return XmxVal;
}
content.append("\n");
/**
* Returns path to the heap dump specified in the conf file.
* @return Path to the heap dump specified in the conf file.
*/
String getHeapDumpPath() {
return heapDumpPath;
}
Files.write(getUserFolderConfFile().toPath(), content.toString().getBytes());
}
/**
* Retrieve the /etc/\*.conf file values pertinent to settings.
* @return The conf file values.
* @throws IOException
*/
private ConfValues getEtcConfValues() throws IOException {
File userConfFile = getUserFolderConfFile();
String[] args = userConfFile.exists() ?
getDefaultsFromFileContents(readConfFile(userConfFile)) :
getDefaultsFromFileContents(readConfFile(getInstallFolderConfFile()));
String heapFile = "";
String memSize = "";
for (String arg : args) {
Matcher memMatch = XMX_REGEX.matcher(arg);
if (memMatch.find()) {
memSize = memMatch.group(XMX_REGEX_PARAM);
continue;
}
Matcher heapFileMatch = HEAP_DUMP_REGEX.matcher(arg);
if (heapFileMatch.find()) {
heapFile = heapFileMatch.group(HEAP_DUMP_REGEX_PARAM);
continue;
}
}
return new ConfValues(memSize, heapFile);
}
/**
* Checks current heap path value to see if it is valid, and displays an error message if invalid.
* @return True if the heap path is valid.
*/
@Messages({
"AutopsyOptionsPanel_isHeapPathValid_selectValidDirectory=Please select an existing directory.",
"AutopsyOptionsPanel_isHeapPathValid_developerMode=Cannot change heap dump path while in developer mode.",
"AutopsyOptionsPanel_isHeapPathValid_not64BitMachine=Changing heap dump path settings only enabled for 64 bit version.",
"AutopsyOPtionsPanel_isHeapPathValid_illegalCharacters=Please select a path with no quotes."
})
private boolean isHeapPathValid() {
if (Version.getBuildType() == Version.Type.DEVELOPMENT) {
heapFieldValidationLabel.setVisible(true);
heapFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_isHeapPathValid_developerMode());
return true;
}
if (!PlatformUtil.is64BitJVM()) {
heapFieldValidationLabel.setVisible(true);
heapFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_isHeapPathValid_not64BitMachine());
return true;
}
//allow blank field as the default will be used
if (StringUtils.isNotBlank(heapDumpFileField.getText())) {
String heapText = heapDumpFileField.getText().trim();
if (heapText.contains("\"") || heapText.contains("'")) {
heapFieldValidationLabel.setVisible(true);
heapFieldValidationLabel.setText(Bundle.AutopsyOPtionsPanel_isHeapPathValid_illegalCharacters());
return false;
}
File curHeapFile = new File(heapText);
if (!curHeapFile.exists() || !curHeapFile.isDirectory()) {
heapFieldValidationLabel.setVisible(true);
heapFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_isHeapPathValid_selectValidDirectory());
return false;
}
}
heapFieldValidationLabel.setVisible(false);
heapFieldValidationLabel.setText("");
return true;
}
/**
@ -295,11 +447,17 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
* options is not present.
*/
private static String[] getDefaultsFromFileContents(List<String> list) {
Optional<String> defaultSettings = list.stream().filter(line -> line.startsWith("default_options=")).findFirst();
Optional<String> defaultSettings = list.stream()
.filter(line -> line.matches(JVM_SETTINGS_REGEX_STR))
.findFirst();
if (defaultSettings.isPresent()) {
return defaultSettings.get().replace("default_options=", "").replaceAll("\"", "").split(" ");
Matcher match = JVM_SETTINGS_REGEX.matcher(defaultSettings.get());
if (match.find()) {
return Commandline.translateCommandline(match.group(JVM_SETTINGS_REGEX_PARAM));
}
}
return new String[]{};
}
@ -347,15 +505,25 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
} catch (IOException ex) {
logger.log(Level.WARNING, "Error loading image from previously saved agency logo path", ex);
}
if (memField.isEnabled()) {
boolean confLoaded = false;
if (isJVMHeapSettingsCapable()) {
try {
initialMemValue = Long.toString(getCurrentJvmMaxMemoryInGB());
ConfValues confValues = getEtcConfValues();
heapDumpFileField.setText(confValues.getHeapDumpPath());
initialMemValue = Long.toString(getCurrentJvmMaxMemoryInGB(confValues.getXmxVal()));
confLoaded = true;
} catch (IOException ex) {
logger.log(Level.SEVERE, "Can't read current Jvm max memory setting from file", ex);
memField.setEnabled(false);
heapDumpFileField.setText(DEFAULT_HEAP_DUMP_FILE_FIELD);
}
memField.setText(initialMemValue);
}
heapDumpBrowseButton.setEnabled(confLoaded);
heapDumpFileField.setEnabled(confLoaded);
setTempDirEnabled();
valid(); //ensure the error messages are up to date
}
@ -459,7 +627,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
reportBranding.setAgencyLogoPath("");
}
UserPreferences.setMaxSolrVMSize((int) solrMaxHeapSpinner.getValue());
if (memField.isEnabled()) { //if the field could of been changed we need to try and save it
if (isJVMHeapSettingsCapable()) { //if the field could of been changed we need to try and save it
try {
writeEtcConfFile();
} catch (IOException ex) {
@ -474,18 +642,12 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
* @return True if valid; false otherwise.
*/
boolean valid() {
boolean valid = true;
if (!isAgencyLogoPathValid()) {
valid = false;
}
if (!isMemFieldValid()) {
valid = false;
}
if (!isLogNumFieldValid()) {
valid = false;
}
boolean agencyValid = isAgencyLogoPathValid();
boolean memFieldValid = isMemFieldValid();
boolean logNumValid = isLogNumFieldValid();
boolean heapPathValid = isHeapPathValid();
return valid;
return agencyValid && memFieldValid && logNumValid && heapPathValid;
}
/**
@ -589,47 +751,41 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
/**
* Listens for registered text fields that have changed and fires a
* PropertyChangeEvent accordingly.
* PropertyChangeEvent accordingly as well as firing an optional additional listener.
*/
private class TextFieldListener implements DocumentListener {
private final Runnable onChange;
@Override
public void insertUpdate(DocumentEvent e) {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
@Override
public void removeUpdate(DocumentEvent e) {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
@Override
public void changedUpdate(DocumentEvent e) {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}
/**
* Listens for changes in the temp directory custom directory text field.
* Main constructor.
* @param onChange Additional listener for change events.
*/
private class TempCustomTextListener extends TextFieldListener {
TextFieldListener(Runnable onChange) {
this.onChange = onChange;
}
private void baseOnChange() {
if (onChange != null) {
onChange.run();
}
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
@Override
public void changedUpdate(DocumentEvent e) {
evaluateTempDirState();
super.changedUpdate(e);
baseOnChange();
}
@Override
public void removeUpdate(DocumentEvent e) {
evaluateTempDirState();
super.changedUpdate(e);
baseOnChange();
}
@Override
public void insertUpdate(DocumentEvent e) {
evaluateTempDirState();
super.changedUpdate(e);
baseOnChange();
}
@ -673,6 +829,10 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
maxMemoryUnitsLabel2 = new javax.swing.JLabel();
solrMaxHeapSpinner = new javax.swing.JSpinner();
solrJVMHeapWarning = new javax.swing.JLabel();
heapFileLabel = new javax.swing.JLabel();
heapDumpFileField = new javax.swing.JTextField();
heapDumpBrowseButton = new javax.swing.JButton();
heapFieldValidationLabel = new javax.swing.JLabel();
tempDirectoryPanel = new javax.swing.JPanel();
tempCustomField = new javax.swing.JTextField();
tempDirectoryBrowseButton = new javax.swing.JButton();
@ -823,6 +983,20 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
org.openide.awt.Mnemonics.setLocalizedText(solrJVMHeapWarning, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.solrJVMHeapWarning.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(heapFileLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapFileLabel.text")); // NOI18N
heapDumpFileField.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapDumpFileField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(heapDumpBrowseButton, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapDumpBrowseButton.text")); // NOI18N
heapDumpBrowseButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
heapDumpBrowseButtonActionPerformed(evt);
}
});
heapFieldValidationLabel.setForeground(new java.awt.Color(255, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(heapFieldValidationLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapFieldValidationLabel.text")); // NOI18N
javax.swing.GroupLayout runtimePanelLayout = new javax.swing.GroupLayout(runtimePanel);
runtimePanel.setLayout(runtimePanelLayout);
runtimePanelLayout.setHorizontalGroup(
@ -851,14 +1025,26 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.addGroup(runtimePanelLayout.createSequentialGroup()
.addGap(23, 23, 23)
.addComponent(memFieldValidationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 478, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap(12, Short.MAX_VALUE))
.addGroup(runtimePanelLayout.createSequentialGroup()
.addGap(18, 18, 18)
.addComponent(solrJVMHeapWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 331, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(44, 44, 44)
.addComponent(logNumAlert)
.addContainerGap())))
.addComponent(restartNecessaryWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 615, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGroup(runtimePanelLayout.createSequentialGroup()
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(restartNecessaryWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 615, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(runtimePanelLayout.createSequentialGroup()
.addComponent(heapFileLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(heapFieldValidationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 478, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(runtimePanelLayout.createSequentialGroup()
.addComponent(heapDumpFileField, javax.swing.GroupLayout.PREFERRED_SIZE, 415, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(heapDumpBrowseButton)))))
.addGap(0, 0, Short.MAX_VALUE))))
);
runtimePanelLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {maxLogFileCount, maxMemoryLabel, totalMemoryLabel});
@ -892,7 +1078,14 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(maxLogFileCount)
.addComponent(logFileCount, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(heapFileLabel)
.addComponent(heapDumpFileField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(heapDumpBrowseButton))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(heapFieldValidationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(restartNecessaryWarning)
.addContainerGap())
);
@ -965,7 +1158,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.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))
.addContainerGap(164, Short.MAX_VALUE))
);
tempDirectoryPanelLayout.setVerticalGroup(
tempDirectoryPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -1162,6 +1355,24 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
evaluateTempDirState();
}//GEN-LAST:event_tempCustomRadioActionPerformed
@Messages({
"AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsTitle=File Already Exists",
"AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsMessage=File already exists. Please select a new location."
})
private void heapDumpBrowseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_heapDumpBrowseButtonActionPerformed
String oldHeapPath = heapDumpFileField.getText();
if (!StringUtils.isBlank(oldHeapPath)) {
heapFileChooser.setCurrentDirectory(new File(oldHeapPath));
}
int returnState = heapFileChooser.showOpenDialog(this);
if (returnState == JFileChooser.APPROVE_OPTION) {
File selectedDirectory = heapFileChooser.getSelectedFile();
heapDumpFileField.setText(selectedDirectory.getAbsolutePath());
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_heapDumpBrowseButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextField agencyLogoPathField;
private javax.swing.JLabel agencyLogoPathFieldValidationLabel;
@ -1170,6 +1381,10 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private javax.swing.JRadioButton defaultLogoRB;
private javax.swing.ButtonGroup displayTimesButtonGroup;
private javax.swing.ButtonGroup fileSelectionButtonGroup;
private javax.swing.JButton heapDumpBrowseButton;
private javax.swing.JTextField heapDumpFileField;
private javax.swing.JLabel heapFieldValidationLabel;
private javax.swing.JLabel heapFileLabel;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextField logFileCount;
private javax.swing.JTextField logNumAlert;

View File

@ -251,3 +251,7 @@ AutopsyOptionsPanel.tempCaseRadio.text=Temp folder in case directory
AutopsyOptionsPanel.tempCustomRadio.text=Custom
AutopsyOptionsPanel.tempCustomField.text=
AutopsyOptionsPanel.tempOnCustomNoPath.text=Please select a path for the custom root temp directory.
AutopsyOptionsPanel.heapDumpFileField.text=
AutopsyOptionsPanel.heapDumpBrowseButton.text=Browse
AutopsyOptionsPanel.heapFileLabel.text=Custom Heap Dump Location:
AutopsyOptionsPanel.heapFieldValidationLabel.text=

View File

@ -12,6 +12,12 @@ AutopsyOptionsPanel.memFieldValidationLabel.noValueEntered.text=No value entered
AutopsyOptionsPanel.memFieldValidationLabel.overMaxMemory.text=Value must be less than the total system memory of {0}GB
# {0} - minimumMemory
AutopsyOptionsPanel.memFieldValidationLabel.underMinMemory.text=Value must be at least {0}GB
AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsMessage=File already exists. Please select a new location.
AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsTitle=File Already Exists
AutopsyOptionsPanel_isHeapPathValid_developerMode=Cannot change heap dump path while in developer mode.
AutopsyOPtionsPanel_isHeapPathValid_illegalCharacters=Please select a path with no quotes.
AutopsyOptionsPanel_isHeapPathValid_not64BitMachine=Changing heap dump path settings only enabled for 64 bit version.
AutopsyOptionsPanel_isHeapPathValid_selectValidDirectory=Please select an existing directory.
AutopsyOptionsPanel_storeTempDir_onChoiceError_description=There was an error updating temporary directory choice selection.
AutopsyOptionsPanel_storeTempDir_onChoiceError_title=Error Saving Temporary Directory Choice
# {0} - path
@ -311,3 +317,7 @@ AutopsyOptionsPanel.tempCaseRadio.text=Temp folder in case directory
AutopsyOptionsPanel.tempCustomRadio.text=Custom
AutopsyOptionsPanel.tempCustomField.text=
AutopsyOptionsPanel.tempOnCustomNoPath.text=Please select a path for the custom root temp directory.
AutopsyOptionsPanel.heapDumpFileField.text=
AutopsyOptionsPanel.heapDumpBrowseButton.text=Browse
AutopsyOptionsPanel.heapFileLabel.text=Custom Heap Dump Location:
AutopsyOptionsPanel.heapFieldValidationLabel.text=

View File

@ -24,6 +24,7 @@ import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
@ -75,6 +76,16 @@ public class Installer extends ModuleInstall {
}
private void setLookAndFeel() {
ImageIcon questionIcon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/question_32.png"));
ImageIcon warningIcon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/warning_32.png"));
ImageIcon informationIcon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/information_32.png"));
ImageIcon errorIcon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error_32.png"));
UIManager.put("OptionPane.errorIcon", errorIcon);
UIManager.put("OptionPane.warningIcon", warningIcon);
UIManager.put("OptionPane.questionIcon", questionIcon);
UIManager.put("OptionPane.informationIcon", informationIcon);
if (System.getProperty("os.name").toLowerCase().contains("mac")) { //NON-NLS
setUnixLookAndFeel();
setModuleSettings("false");

View File

@ -115,7 +115,7 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
return true;
} else {
// otherwise, just show host level
tskCase.getHostManager().getHosts().stream()
tskCase.getHostManager().getAllHosts().stream()
.map(HostGrouping::new)
.sorted()
.forEach(list::add);

View File

@ -376,6 +376,10 @@ TagNode.propertySheet.origNameDisplayName=Original Name
TagsNode.displayName.text=Tags
TagsNode.createSheet.name.name=Name
TagsNode.createSheet.name.displayName=Name
UnsupportedContentNode.createSheet.name.desc=no description
UnsupportedContentNode.createSheet.name.displayName=Name
UnsupportedContentNode.createSheet.name.name=Name
UnsupportedContentNode.displayName=Unsupported Content
ViewsNode.name.text=Views
ViewsNode.createSheet.name.name=Name
ViewsNode.createSheet.name.displayName=Name

View File

@ -18,6 +18,8 @@
*/
package org.sleuthkit.autopsy.datamodel;
import org.sleuthkit.autopsy.datamodel.OsAccounts.OsAccountNode;
/**
* Visitor Pattern interface that goes over Content nodes in the data source
* area of the tree.
@ -50,6 +52,9 @@ interface ContentNodeVisitor<T> {
T visit(BlackboardArtifactNode bban);
T visit(UnsupportedContentNode ucn);
T visit(OsAccountNode bban);
/**
* Visitor with an implementable default behavior for all types. Override
@ -122,5 +127,15 @@ interface ContentNodeVisitor<T> {
public T visit(BlackboardArtifactNode bban) {
return defaultVisit(bban);
}
@Override
public T visit(UnsupportedContentNode ucn) {
return defaultVisit(ucn);
}
@Override
public T visit(OsAccountNode bban) {
return defaultVisit(bban);
}
}
}

View File

@ -32,6 +32,7 @@ import org.sleuthkit.datamodel.Pool;
import org.sleuthkit.datamodel.SlackFile;
import org.sleuthkit.datamodel.SleuthkitItemVisitor;
import org.sleuthkit.datamodel.SleuthkitVisitableItem;
import org.sleuthkit.datamodel.UnsupportedContent;
import org.sleuthkit.datamodel.VirtualDirectory;
import org.sleuthkit.datamodel.Volume;
@ -100,6 +101,11 @@ public class CreateSleuthkitNodeVisitor extends SleuthkitItemVisitor.Default<Abs
return new BlackboardArtifactNode(art);
}
@Override
public AbstractContentNode<? extends Content> visit(UnsupportedContent uc) {
return new UnsupportedContentNode(uc);
}
@Override
protected AbstractContentNode<? extends Content> defaultVisit(SleuthkitVisitableItem di) {
throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(),

View File

@ -85,7 +85,7 @@ public class DataSourcesByTypeNode extends DisplayableItemNode {
@Override
protected boolean createKeys(List<HostDataSources> toPopulate) {
try {
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getHosts().stream()
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getAllHosts().stream()
.map(HostDataSources::new)
.sorted()
.forEach(toPopulate::add);

View File

@ -203,6 +203,11 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(DataSourcesByTypeNode node);
/*
* Unsupported node
*/
T visit(UnsupportedContentNode ucn);
/**
* Visitor with an implementable default behavior for all types. Override
* specific visit types to not use the default behavior.
@ -574,5 +579,10 @@ public interface DisplayableItemNodeVisitor<T> {
public T visit(PersonGroupingNode node) {
return defaultVisit(node);
}
@Override
public T visit(UnsupportedContentNode node) {
return defaultVisit(node);
}
}
}

View File

@ -32,6 +32,7 @@ import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.logging.Level;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
@ -77,7 +78,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
*/
public static final Map<String, String> parsePath(String path) {
Map<String, String> parsed = new HashMap<>();
String[] split = path.split(MAIL_PATH_SEPARATOR);
String[] split = path == null ? new String[0] : path.split(MAIL_PATH_SEPARATOR);
if (split.length < 4) {
parsed.put(MAIL_ACCOUNT, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text"));
parsed.put(MAIL_FOLDER, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text"));
@ -147,55 +148,59 @@ public class EmailExtracted implements AutopsyVisitableItem {
@SuppressWarnings("deprecation")
public void update() {
// clear cache if no case
if (skCase == null) {
synchronized (accounts) {
accounts.clear();
}
if (skCase == null) {
return;
}
int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID();
// get artifact id and path (if present) of all email artifacts
int emailArtifactId = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID();
int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID();
String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS
+ "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS
+ "attribute_type_id=" + pathAttrId //NON-NLS
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
if (filteringDSObjId > 0) {
query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
}
String query = "SELECT \n" +
" art.artifact_id AS artifact_id,\n" +
" (SELECT value_text FROM blackboard_attributes attr\n" +
" WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + pathAttrId + "\n" +
" LIMIT 1) AS value_text\n" +
"FROM \n" +
" blackboard_artifacts art\n" +
" WHERE art.artifact_type_id = " + emailArtifactId + "\n" +
((filteringDSObjId > 0) ? " AND art.data_source_obj_id = " + filteringDSObjId : "");
// form hierarchy of account -> folder -> account id
Map<String, Map<String, List<Long>>> newMapping = new HashMap<>();
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
ResultSet resultSet = dbQuery.getResultSet();
synchronized (accounts) {
while (resultSet.next()) {
final String path = resultSet.getString("value_text"); //NON-NLS
final long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
final Map<String, String> parsedPath = parsePath(path);
final String account = parsedPath.get(MAIL_ACCOUNT);
final String folder = parsedPath.get(MAIL_FOLDER);
Long artifactId = resultSet.getLong("artifact_id");
Map<String, String> accountFolderMap = parsePath(resultSet.getString("value_text"));
String account = accountFolderMap.get(MAIL_ACCOUNT);
String folder = accountFolderMap.get(MAIL_FOLDER);
Map<String, List<Long>> folders = accounts.get(account);
if (folders == null) {
folders = new LinkedHashMap<>();
accounts.put(account, folders);
}
List<Long> messages = folders.get(folder);
if (messages == null) {
messages = new ArrayList<>();
folders.put(folder, messages);
}
Map<String, List<Long>> folders = newMapping.computeIfAbsent(account, (str) -> new LinkedHashMap<>());
List<Long> messages = folders.computeIfAbsent(folder, (str) -> new ArrayList<>());
messages.add(artifactId);
}
}
} catch (TskCoreException | SQLException ex) {
logger.log(Level.WARNING, "Cannot initialize email extraction: ", ex); //NON-NLS
}
synchronized (accounts) {
accounts.clear();
accounts.putAll(newMapping);
}
setChanged();
notifyObservers();
}
}
/**
* Mail root node grouping all mail accounts, supports account-> folder
* structure

View File

@ -51,7 +51,7 @@ public class HostDataSources implements AutopsyVisitableItem, Comparable<HostDat
@Override
public int hashCode() {
return Objects.hashCode(this.host == null ? 0 : this.host.getId());
return Objects.hashCode(this.host == null ? 0 : this.host.getHostId());
}
@Override
@ -66,8 +66,8 @@ public class HostDataSources implements AutopsyVisitableItem, Comparable<HostDat
return false;
}
final HostDataSources other = (HostDataSources) obj;
long thisId = (this.getHost() == null) ? 0 : this.getHost().getId();
long otherId = (other.getHost() == null) ? 0 : other.getHost().getId();
long thisId = (this.getHost() == null) ? 0 : this.getHost().getHostId();
long otherId = (other.getHost() == null) ? 0 : other.getHost().getHostId();
return thisId == otherId;
}

View File

@ -51,7 +51,7 @@ public class HostGrouping implements AutopsyVisitableItem, Comparable<HostGroupi
@Override
public int hashCode() {
return Objects.hashCode(this.host == null ? 0 : this.host.getId());
return Objects.hashCode(this.host == null ? 0 : this.host.getHostId());
}
@Override
@ -66,8 +66,8 @@ public class HostGrouping implements AutopsyVisitableItem, Comparable<HostGroupi
return false;
}
final HostGrouping other = (HostGrouping) obj;
long thisId = (this.getHost() == null) ? 0 : this.getHost().getId();
long otherId = (other.getHost() == null) ? 0 : other.getHost().getId();
long thisId = (this.getHost() == null) ? 0 : this.getHost().getHostId();
long otherId = (other.getHost() == null) ? 0 : other.getHost().getHostId();
return thisId == otherId;
}

View File

@ -78,13 +78,16 @@ public class HostNode extends DisplayableItemNode {
}
/**
* Listener for handling DATA_SOURCE_ADDED events.
* Listener for handling DATA_SOURCE_ADDED / HOST_DELETED events.
* A host may have been deleted as part of a merge, which means its data sources could
* have moved to a different host requiring a refresh.
*/
private final PropertyChangeListener dataSourceAddedPcl = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())
|| eventType.equals(Case.Events.HOSTS_DELETED.toString())) {
refresh(true);
}
}
@ -92,12 +95,12 @@ public class HostNode extends DisplayableItemNode {
@Override
protected void addNotify() {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), dataSourceAddedPcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_DELETED), dataSourceAddedPcl);
}
@Override
protected void removeNotify() {
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), dataSourceAddedPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_DELETED), dataSourceAddedPcl);
}
@Override
@ -176,7 +179,7 @@ public class HostNode extends DisplayableItemNode {
String eventType = evt.getPropertyName();
if (hostId != null && eventType.equals(Case.Events.HOSTS_CHANGED.toString()) && evt instanceof HostsChangedEvent) {
((HostsChangedEvent) evt).getNewValue().stream()
.filter(h -> h != null && h.getId() == hostId)
.filter(h -> h != null && h.getHostId() == hostId)
.findFirst()
.ifPresent((newHost) -> {
setName(newHost.getName());
@ -242,7 +245,7 @@ public class HostNode extends DisplayableItemNode {
super(children,
host == null ? Lookups.fixed(displayName) : Lookups.fixed(host, displayName));
hostId = host == null ? null : host.getId();
hostId = host == null ? null : host.getHostId();
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.HOSTS_CHANGED),
WeakListeners.propertyChange(hostChangePcl, this));
super.setName(displayName);
@ -297,7 +300,7 @@ public class HostNode extends DisplayableItemNode {
// Add the appropriate Person action
Optional<Person> parent;
try {
parent = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getPerson(this.host);
parent = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().getPerson(this.host);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.WARNING, String.format("Error fetching parent person of host: %s", this.host.getName() == null ? "<null>" : this.host.getName()), ex);
return new Action[0];

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@ -29,18 +30,26 @@ import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
import javax.swing.Action;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups;
import org.openide.util.WeakListeners;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.OsAccountRealm;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException;
@ -52,6 +61,7 @@ public final class OsAccounts implements AutopsyVisitableItem {
private static final Logger logger = Logger.getLogger(OsAccounts.class.getName());
private static final String ICON_PATH = "org/sleuthkit/autopsy/images/os-account.png";
private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
private static final String REALM_DATA_AVAILABLE_EVENT = "REALM_DATA_AVAILABLE_EVENT";
private SleuthkitCase skCase;
private final long filteringDSObjId;
@ -114,7 +124,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 +139,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);
}
@ -140,13 +151,13 @@ public final class OsAccounts implements AutopsyVisitableItem {
@Override
protected boolean createKeys(List<OsAccount> list) {
if(skCase != null) {
if (skCase != null) {
try {
if (filteringDSObjId == 0) {
list.addAll(skCase.getOsAccountManager().getAccounts());
list.addAll(skCase.getOsAccountManager().getOsAccounts());
} else {
Host host = skCase.getHostManager().getHost(skCase.getDataSource(filteringDSObjId));
list.addAll(skCase.getOsAccountManager().getAccounts(host));
Host host = skCase.getHostManager().getHostByDataSource(skCase.getDataSource(filteringDSObjId));
list.addAll(skCase.getOsAccountManager().getOsAccounts(host));
}
} catch (TskCoreException | TskDataException ex) {
logger.log(Level.SEVERE, "Unable to retrieve list of OsAccounts for case", ex);
@ -165,35 +176,52 @@ public final class OsAccounts implements AutopsyVisitableItem {
/**
* An OsAccount leaf Node.
*/
public static final class OsAccountNode extends DisplayableItemNode {
public static final class OsAccountNode extends AbstractContentNode<OsAccount> {
private OsAccount account;
private final PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if(((OsAccountChangedEvent)evt).getOsAccount().getId() == account.getId()) {
if (evt.getPropertyName().equals(Case.Events.OS_ACCOUNT_CHANGED.name())) {
if (((OsAccountChangedEvent) evt).getOsAccount().getId() == account.getId()) {
// Update the account node to the new one
account = ((OsAccountChangedEvent)evt).getOsAccount();
account = ((OsAccountChangedEvent) evt).getOsAccount();
updateSheet();
}
} else if (evt.getPropertyName().equals(REALM_DATA_AVAILABLE_EVENT)) {
OsAccountRealm realm = (OsAccountRealm) evt.getNewValue();
// Currently only 0 or 1 names are supported, this will need
// to be modified if that changes.
List<String> realmNames = realm.getRealmNames();
if (!realmNames.isEmpty()) {
updateSheet(new NodeProperty<>(
Bundle.OsAccounts_accountRealmNameProperty_name(),
Bundle.OsAccounts_accountRealmNameProperty_displayName(),
Bundle.OsAccounts_accountRealmNameProperty_desc(),
""));
}
}
}
};
private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
/**
* Constructs a new OsAccountNode.
*
* @param account Node object.
*/
OsAccountNode(OsAccount account) {
super(Children.LEAF, Lookups.fixed(account));
super(account);
this.account = account;
setName(account.getName());
setDisplayName(account.getName());
setIconBaseWithExtension(ICON_PATH);
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), listener);
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), weakListener);
}
@Override
@ -211,6 +239,15 @@ public final class OsAccounts implements AutopsyVisitableItem {
return getClass().getName();
}
/**
* Returns the OsAccount associated with this node.
*
* @return
*/
OsAccount getOsAccount() {
return account;
}
@Messages({
"OsAccounts_accountNameProperty_name=Name",
"OsAccounts_accountNameProperty_displayName=Name",
@ -254,13 +291,13 @@ public final class OsAccounts implements AutopsyVisitableItem {
Bundle.OsAccounts_loginNameProperty_displayName(),
Bundle.OsAccounts_loginNameProperty_desc(),
optional.isPresent() ? optional.get() : ""));
optional = account.getRealm().getRealmName();
// Fill with empty string, fetch on background task.
String realmName = "";
propertiesSet.put(new NodeProperty<>(
Bundle.OsAccounts_accountRealmNameProperty_name(),
Bundle.OsAccounts_accountRealmNameProperty_displayName(),
Bundle.OsAccounts_accountRealmNameProperty_desc(),
optional.isPresent() ? optional.get() : ""));
realmName));
Optional<Long> creationTimeValue = account.getCreationTime();
String timeDisplayStr
@ -272,6 +309,8 @@ public final class OsAccounts implements AutopsyVisitableItem {
Bundle.OsAccounts_createdTimeProperty_desc(),
timeDisplayStr));
backgroundTasksPool.submit(new GetOsAccountRealmTask(new WeakReference<>(this), weakListener));
return sheet;
}
@ -283,5 +322,78 @@ public final class OsAccounts implements AutopsyVisitableItem {
return actionsList.toArray(new Action[actionsList.size()]);
}
@Override
protected List<Tag> getAllTagsFromDatabase() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
return null;
}
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
return null;
}
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
}
@Override
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) {
return null;
}
@Override
public <T> T accept(ContentNodeVisitor<T> visitor) {
return visitor.visit(this);
}
/**
* Task for grabbing the osAccount realm.
*/
static class GetOsAccountRealmTask implements Runnable {
private final WeakReference<OsAccountNode> weakNodeRef;
private final PropertyChangeListener listener;
/**
* Construct a new task.
*
* @param weakContentRef
* @param listener
*/
GetOsAccountRealmTask(WeakReference<OsAccountNode> weakContentRef, PropertyChangeListener listener) {
this.weakNodeRef = weakContentRef;
this.listener = listener;
}
@Override
public void run() {
OsAccountNode node = weakNodeRef.get();
if (node == null) {
return;
}
try {
long realmId = node.getOsAccount().getRealmId();
OsAccountRealm realm = Case.getCurrentCase().getSleuthkitCase().getOsAccountRealmManager().getRealmByRealmId(realmId);
if (listener != null && realm != null) {
listener.propertyChange(new PropertyChangeEvent(
AutopsyEvent.SourceType.LOCAL.toString(),
REALM_DATA_AVAILABLE_EVENT,
null, realm));
}
} catch (TskCoreException ex) {
Exceptions.printStackTrace(ex);
}
}
}
}
}

View File

@ -52,7 +52,7 @@ public class PersonGrouping implements AutopsyVisitableItem, Comparable<PersonGr
@Override
public int hashCode() {
return Objects.hashCode(this.person == null ? 0 : this.person.getId());
return Objects.hashCode(this.person == null ? 0 : this.person.getPersonId());
}
@Override
@ -67,8 +67,8 @@ public class PersonGrouping implements AutopsyVisitableItem, Comparable<PersonGr
return false;
}
final PersonGrouping other = (PersonGrouping) obj;
long thisId = (this.getPerson() == null) ? 0 : this.getPerson().getId();
long otherId = (other.getPerson() == null) ? 0 : other.getPerson().getId();
long thisId = (this.getPerson() == null) ? 0 : this.getPerson().getPersonId();
long otherId = (other.getPerson() == null) ? 0 : other.getPerson().getPersonId();
return thisId == otherId;
}

View File

@ -147,7 +147,7 @@ public class PersonGroupingNode extends DisplayableItemNode {
String eventType = evt.getPropertyName();
if (personId != null && eventType.equals(Case.Events.PERSONS_CHANGED.toString()) && evt instanceof PersonsChangedEvent) {
((PersonsChangedEvent) evt).getNewValue().stream()
.filter(p -> p != null && p.getId() == personId)
.filter(p -> p != null && p.getPersonId() == personId)
.findFirst()
.ifPresent((newPerson) -> {
setName(newPerson.getName());
@ -191,7 +191,7 @@ public class PersonGroupingNode extends DisplayableItemNode {
super.setDisplayName(displayName);
this.setIconBaseWithExtension(ICON_PATH);
this.person = person;
this.personId = person == null ? null : person.getId();
this.personId = person == null ? null : person.getPersonId();
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.PERSONS_CHANGED),
WeakListeners.propertyChange(personChangePcl, this));
}

View File

@ -0,0 +1,186 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Action;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR;
import org.sleuthkit.datamodel.UnsupportedContent;
import org.sleuthkit.datamodel.Tag;
/**
* This class is used to represent the "Node" for an unsupported content object.
*/
public class UnsupportedContentNode extends AbstractContentNode<UnsupportedContent> {
/**
*
* @param unsupportedContent underlying Content instance
*/
@NbBundle.Messages({
"UnsupportedContentNode.displayName=Unsupported Content",
})
public UnsupportedContentNode(UnsupportedContent unsupportedContent) {
super(unsupportedContent);
// set name, display name, and icon
this.setDisplayName(Bundle.UnsupportedContentNode_displayName());
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon.png"); //NON-NLS
}
/**
* Right click action for UnsupportedContentNode node
*
* @param popup
*
* @return
*/
@Override
public Action[] getActions(boolean popup) {
List<Action> actionsList = new ArrayList<>();
for (Action a : super.getActions(true)) {
actionsList.add(a);
}
return actionsList.toArray(new Action[actionsList.size()]);
}
@NbBundle.Messages({
"UnsupportedContentNode.createSheet.name.name=Name",
"UnsupportedContentNode.createSheet.name.displayName=Name",
"UnsupportedContentNode.createSheet.name.desc=no description",
})
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
sheetSet.put(new NodeProperty<>(Bundle.UnsupportedContentNode_createSheet_name_name(),
Bundle.UnsupportedContentNode_createSheet_name_displayName(),
Bundle.UnsupportedContentNode_createSheet_name_desc(),
this.getDisplayName()));
return sheet;
}
@Override
public <T> T accept(ContentNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public boolean isLeafTypeNode() {
return false;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public String getItemType() {
return getClass().getName();
}
/**
* Reads and returns a list of all tags associated with this content node.
*
* Null implementation of an abstract method.
*
* @return list of tags associated with the node.
*/
@Override
protected List<Tag> getAllTagsFromDatabase() {
return new ArrayList<>();
}
/**
* Returns correlation attribute instance for the underlying content of the
* node.
*
* Null implementation of an abstract method.
*
* @return correlation attribute instance for the underlying content of the
* node.
*/
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
return null;
}
/**
* Returns Score property for the node.
*
* Null implementation of an abstract method.
*
* @param tags list of tags.
*
* @return Score property for the underlying content of the node.
*/
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR);
}
/**
* Returns comment property for the node.
*
* Null implementation of an abstract method.
*
* @param tags list of tags
* @param attribute correlation attribute instance
*
* @return Comment property for the underlying content of the node.
*/
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
}
/**
* Returns occurrences/count property for the node.
*
* Null implementation of an abstract method.
*
* @param attributeType the type of the attribute to count
* @param attributeValue the value of the attribute to coun
* @param defaultDescription a description to use when none is determined by
* the getCountPropertyAndDescription method
*
* @return count property for the underlying content of the node.
*/
@Override
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) {
return Pair.of(-1L, NO_DESCR);
}
}

View File

@ -65,8 +65,8 @@ public class AssociateNewPersonAction extends AbstractAction {
try {
newPersonName = getAddDialogName();
if (StringUtils.isNotBlank(newPersonName)) {
Person person = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().createPerson(newPersonName);
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().setPerson(host, person);
Person person = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().newPerson(newPersonName);
Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().setPerson(host, person);
}
} catch (NoCurrentCaseException | TskCoreException ex) {
String hostName = this.host == null || this.host.getName() == null ? "" : this.host.getName();

View File

@ -65,7 +65,7 @@ public class AssociatePersonAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
try {
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().setPerson(host, person);
Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().setPerson(host, person);
} catch (NoCurrentCaseException | TskCoreException ex) {
String hostName = this.host == null || this.host.getName() == null ? "" : this.host.getName();
String personName = this.person == null || this.person.getName() == null ? "" : this.person.getName();

View File

@ -89,7 +89,7 @@ public class ManageHostsDialog extends javax.swing.JDialog {
@Override
public int hashCode() {
int hash = 5;
hash = 89 * hash + Objects.hashCode(this.host == null ? 0 : this.host.getId());
hash = 89 * hash + Objects.hashCode(this.host == null ? 0 : this.host.getHostId());
return hash;
}
@ -109,7 +109,7 @@ public class ManageHostsDialog extends javax.swing.JDialog {
return this.host == null && other.getHost() == null;
}
return this.host.getId() == other.getHost().getId();
return this.host.getHostId() == other.getHost().getHostId();
}
}
@ -166,8 +166,8 @@ public class ManageHostsDialog extends javax.swing.JDialog {
if (newHostName != null) {
Long selectedId = null;
try {
Host newHost = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().createHost(newHostName);
selectedId = newHost == null ? null : newHost.getId();
Host newHost = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().newHost(newHostName);
selectedId = newHost == null ? null : newHost.getHostId();
} catch (NoCurrentCaseException | TskCoreException e) {
logger.log(Level.WARNING, String.format("Unable to add new host '%s' at this time.", newHostName), e);
}
@ -215,7 +215,7 @@ public class ManageHostsDialog extends javax.swing.JDialog {
continue;
}
if (host.getId() == selectedId) {
if (host.getHostId() == selectedId) {
hostList.setSelectedIndex(i);
return;
}
@ -234,15 +234,14 @@ public class ManageHostsDialog extends javax.swing.JDialog {
if (selectedHost != null) {
String newHostName = getAddEditDialogName(selectedHost);
if (newHostName != null) {
selectedHost.setName(newHostName);
try {
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().updateHost(selectedHost);
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().updateHostName(selectedHost, newHostName);
} catch (NoCurrentCaseException | TskCoreException e) {
logger.log(Level.WARNING, String.format("Unable to update host '%s' with id: %d at this time.", selectedHost.getName(), selectedHost.getId()), e);
logger.log(Level.WARNING, String.format("Unable to update host '%s' with id: %d at this time.", selectedHost.getName(), selectedHost.getHostId()), e);
}
HostListItem selectedItem = hostList.getSelectedValue();
Long selectedId = selectedItem == null || selectedItem.getHost() == null ? null : selectedItem.getHost().getId();
Long selectedId = selectedItem == null || selectedItem.getHost() == null ? null : selectedItem.getHost().getHostId();
refresh();
@ -322,7 +321,7 @@ public class ManageHostsDialog extends javax.swing.JDialog {
Map<Host, List<DataSource>> hostMapping = new HashMap<>();
try {
SleuthkitCase curCase = Case.getCurrentCaseThrows().getSleuthkitCase();
List<Host> hosts = curCase.getHostManager().getHosts();
List<Host> hosts = curCase.getHostManager().getAllHosts();
List<DataSource> dataSources = curCase.getDataSources();
if (dataSources != null) {

View File

@ -67,7 +67,7 @@ public class MergeHostMenuAction extends AbstractAction implements Presenter.Pop
// Get a list of all other hosts
List<Host> otherHosts = Collections.emptyList();
try {
otherHosts = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getHosts();
otherHosts = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getAllHosts();
otherHosts.remove(sourceHost);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.WARNING, "Error getting hosts for case.", ex);

View File

@ -64,7 +64,7 @@ public class RemoveParentPersonAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
try {
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().setPerson(host, null);
Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().setPerson(host, null);
} catch (NoCurrentCaseException | TskCoreException ex) {
String hostName = this.host == null || this.host.getName() == null ? "" : this.host.getName();
logger.log(Level.WARNING, String.format("Unable to remove parent from host: %s", hostName), ex);

View File

@ -70,7 +70,7 @@ public final class IconsUtil {
} else if (typeID == ARTIFACT_TYPE.TSK_SPEED_DIAL_ENTRY.getTypeID()) {
imageFile = "speeddialentry.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING.getTypeID()) {
imageFile = "bluetooth.png"; //NON-NLS
imageFile = "Bluetooth.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_GPS_BOOKMARK.getTypeID()) {
imageFile = "gpsfav.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION.getTypeID()) {

View File

@ -76,6 +76,8 @@ ExcelExportAction_exportToXLSX_gatheringTabData=Fetching Data for {0} Tab...
ExcelExportAction_exportToXLSX_writingToFile=Writing to File...
ExcelExportAction_getXLSXPath_directory=DataSourceSummary
ExcelExportAction_moduleName=Data Source Summary
ExcelExportAction_runXLSXExport_errorMessage=There was an error while exporting.
ExcelExportAction_runXLSXExport_errorTitle=Error While Exporting
ExcelExportAction_runXLSXExport_progressCancelActionTitle=Cancelling...
ExcelExportAction_runXLSXExport_progressCancelTitle=Cancel
# {0} - dataSource
@ -114,6 +116,12 @@ SizeRepresentationUtil_units_megabytes=MB
SizeRepresentationUtil_units_petabytes=PB
SizeRepresentationUtil_units_terabytes=TB
TimelinePanel_earliestLabel_title=Earliest
TimelinePanel_getExports_activityRange=Activity Range
TimelinePanel_getExports_chartName=Last 30 Days
TimelinePanel_getExports_dateColumnHeader=Date
TimelinePanel_getExports_earliest=Earliest:
TimelinePanel_getExports_latest=Latest:
TimelinePanel_getExports_sheetName=Timeline
TimelinePanel_latestLabel_title=Latest
TimlinePanel_last30DaysChart_artifactEvts_title=Result Events
TimlinePanel_last30DaysChart_fileEvts_title=File Events
@ -128,6 +136,7 @@ TypesPanel_fileMimeTypesChart_notAnalyzed_title=Not Analyzed
TypesPanel_fileMimeTypesChart_other_title=Other
TypesPanel_fileMimeTypesChart_title=File Types
TypesPanel_fileMimeTypesChart_unknown_title=Unknown
TypesPanel_fileMimeTypesChart_valueLabel=Count
TypesPanel_fileMimeTypesChart_videos_title=Videos
TypesPanel_filesByCategoryTable_allocatedRow_title=Allocated Files
TypesPanel_filesByCategoryTable_directoryRow_title=Directories

View File

@ -18,7 +18,6 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
@ -34,6 +33,7 @@ import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.logging.Level;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.openide.util.NbBundle;
@ -186,7 +186,9 @@ class ExcelExportAction implements Consumer<DataSource> {
"# {0} - dataSource",
"ExcelExportAction_runXLSXExport_progressTitle=Exporting {0} to XLSX",
"ExcelExportAction_runXLSXExport_progressCancelTitle=Cancel",
"ExcelExportAction_runXLSXExport_progressCancelActionTitle=Cancelling..."
"ExcelExportAction_runXLSXExport_progressCancelActionTitle=Cancelling...",
"ExcelExportAction_runXLSXExport_errorTitle=Error While Exporting",
"ExcelExportAction_runXLSXExport_errorMessage=There was an error while exporting.",
})
private void runXLSXExport(DataSource dataSource, File path) {
@ -213,6 +215,10 @@ class ExcelExportAction implements Consumer<DataSource> {
get();
} catch (ExecutionException ex) {
logger.log(Level.WARNING, "Error while trying to export data source summary to xlsx.", ex);
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
Bundle.ExcelExportAction_runXLSXExport_errorMessage(),
Bundle.ExcelExportAction_runXLSXExport_errorTitle(),
JOptionPane.ERROR_MESSAGE);
} catch (InterruptedException | CancellationException ex) {
// no op on cancellation
} finally {

View File

@ -39,14 +39,20 @@ import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineDataSourceUtils
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.DailyActivityAmount;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.BarChartItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.BarChartSeries;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.OrderedKey;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.BarChartItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.KeyValueItemExportable;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.TitledExportable;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel;
@ -70,7 +76,9 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
private static final Logger logger = Logger.getLogger(TimelinePanel.class.getName());
private static final long serialVersionUID = 1L;
private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat("MMM d, yyyy");
private static final String EARLIEST_LATEST_FORMAT_STR = "MMM d, yyyy";
private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat(EARLIEST_LATEST_FORMAT_STR);
private static final DateFormat CHART_FORMAT = getUtcFormat("MMM d, yyyy");
private static final int MOST_RECENT_DAYS_COUNT = 30;
@ -94,6 +102,8 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
// all loadable components on this tab
private final List<LoadableComponent<?>> loadableComponents = Arrays.asList(earliestLabel, latestLabel, last30DaysChart);
private final DataFetcher<DataSource, TimelineSummaryData> dataFetcher;
// actions to load data for this tab
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
@ -107,12 +117,11 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
public TimelinePanel(TimelineSummary timelineData) {
super(timelineData);
dataFetcher = (dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT);
// set up data acquisition methods
dataFetchComponents = Arrays.asList(
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT),
(result) -> handleResult(result))
);
new DataFetchWorker.DataFetchComponents<>(dataFetcher, (result) -> handleResult(result)));
initComponents();
}
@ -138,9 +147,11 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
* data to be displayed as a bar chart.
*
* @param recentDaysActivity The data retrieved from TimelineSummary.
* @param showIntermediateDates If true, shows all dates. If false, shows
* only first and last date.
* @return The data to be displayed in the BarChart.
*/
private List<BarChartSeries> parseChartData(List<DailyActivityAmount> recentDaysActivity) {
private List<BarChartSeries> parseChartData(List<DailyActivityAmount> recentDaysActivity, boolean showIntermediateDates) {
// if no data, return null indicating no result.
if (CollectionUtils.isEmpty(recentDaysActivity)) {
return null;
@ -155,7 +166,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
long fileAmt = curItem.getFileActivityCount();
long artifactAmt = curItem.getArtifactActivityCount() * 100;
String formattedDate = (i == 0 || i == recentDaysActivity.size() - 1)
String formattedDate = (showIntermediateDates || i == 0 || i == recentDaysActivity.size() - 1)
? formatDate(curItem.getDay(), CHART_FORMAT) : "";
OrderedKey thisKey = new OrderedKey(formattedDate, i);
@ -182,7 +193,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
private void handleResult(DataFetchResult<TimelineSummaryData> result) {
earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT)));
latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT)));
last30DaysChart.showDataFetchResult(DataFetchResult.getSubResult(result, r -> parseChartData(r.getMostRecentDaysActivity())));
last30DaysChart.showDataFetchResult(DataFetchResult.getSubResult(result, r -> parseChartData(r.getMostRecentDaysActivity(), false)));
if (result != null
&& result.getResultType() == DataFetchResult.ResultType.SUCCESS
@ -282,11 +293,43 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
super.close();
}
/**
* Create a default cell model to be use with excel export in the earliest /
* latest date format.
*
* @param date The date.
* @return The cell model.
*/
private static DefaultCellModel<?> getEarliestLatestCell(Date date) {
return new DefaultCellModel<>(date, (dt) -> dt == null ? "" : EARLIEST_LATEST_FORMAT.format(dt), EARLIEST_LATEST_FORMAT_STR);
}
@Messages({
"TimelinePanel_getExports_sheetName=Timeline",
"TimelinePanel_getExports_activityRange=Activity Range",
"TimelinePanel_getExports_earliest=Earliest:",
"TimelinePanel_getExports_latest=Latest:",
"TimelinePanel_getExports_dateColumnHeader=Date",
"TimelinePanel_getExports_chartName=Last 30 Days",})
@Override
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
TimelineSummaryData summaryData = getFetchResult(dataFetcher, "Timeline", dataSource);
if (summaryData == null) {
return Collections.emptyList();
}
return Arrays.asList(
new ExcelSpecialFormatExport(Bundle.TimelinePanel_getExports_sheetName(),
Arrays.asList(
new TitledExportable(Bundle.TimelinePanel_getExports_activityRange(), Collections.emptyList()),
new KeyValueItemExportable(Bundle.TimelinePanel_getExports_earliest(), getEarliestLatestCell(summaryData.getMinDate())),
new KeyValueItemExportable(Bundle.TimelinePanel_getExports_latest(), getEarliestLatestCell(summaryData.getMaxDate())),
new BarChartExport(Bundle.TimelinePanel_getExports_dateColumnHeader(),
"#,###",
Bundle.TimelinePanel_getExports_chartName(),
parseChartData(summaryData.getMostRecentDaysActivity(), true)))));
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always

View File

@ -47,8 +47,9 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel.PieChartItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartItem;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
@ -64,6 +65,7 @@ import org.sleuthkit.datamodel.TskCoreException;
"TypesPanel_filesByCategoryTable_slackRow_title=Slack Files",
"TypesPanel_filesByCategoryTable_directoryRow_title=Directories",
"TypesPanel_fileMimeTypesChart_title=File Types",
"TypesPanel_fileMimeTypesChart_valueLabel=Count",
"TypesPanel_fileMimeTypesChart_audio_title=Audio",
"TypesPanel_fileMimeTypesChart_documents_title=Documents",
"TypesPanel_fileMimeTypesChart_executables_title=Executables",
@ -197,6 +199,8 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
private final DataFetcher<DataSource, String> osFetcher;
private final DataFetcher<DataSource, Long> sizeFetcher;
private final DataFetcher<DataSource, TypesPieChartData> typesFetcher;
private final DataFetcher<DataSource, Long> allocatedFetcher;
private final DataFetcher<DataSource, Long> unallocatedFetcher;
private final DataFetcher<DataSource, Long> slackFetcher;
@ -262,6 +266,8 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
this.sizeFetcher = (dataSource) -> dataSource == null ? null : dataSource.getSize();
this.typesFetcher = (dataSource) -> getMimeTypeCategoriesModel(mimeTypeData, dataSource);
this.allocatedFetcher = (dataSource) -> typeData.getCountOfAllocatedFiles(dataSource);
this.unallocatedFetcher = (dataSource) -> typeData.getCountOfUnallocatedFiles(dataSource);
this.slackFetcher = (dataSource) -> typeData.getCountOfSlackFiles(dataSource);
@ -274,9 +280,7 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
(sizeResult) -> sizeLabel.showDataFetchResult(
DataFetchResult.getSubResult(sizeResult,
size -> SizeRepresentationUtil.getSizeString(size, INTEGER_SIZE_FORMAT, false)))),
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> getMimeTypeCategoriesModel(mimeTypeData, dataSource),
this::showMimeTypeCategories),
new DataFetchWorker.DataFetchComponents<>(typesFetcher, this::showMimeTypeCategories),
new DataFetchWorker.DataFetchComponents<>(allocatedFetcher,
countRes -> allocatedLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))),
new DataFetchWorker.DataFetchComponents<>(unallocatedFetcher,
@ -443,12 +447,23 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
return Collections.emptyList();
}
// Retrieve data to create the types pie chart
TypesPieChartData typesData = TypesPanel.getFetchResult(typesFetcher, "Types", dataSource);
PieChartExport typesChart = (typesData == null || !typesData.isUsefulContent()) ? null :
new PieChartExport(
Bundle.TypesPanel_fileMimeTypesChart_title(),
Bundle.TypesPanel_fileMimeTypesChart_valueLabel(),
"#,###",
Bundle.TypesPanel_fileMimeTypesChart_title(),
typesData.getPieSlices());
return Arrays.asList(new ExcelSpecialFormatExport(Bundle.TypesPanel_excelTabName(),
Stream.of(
getStrExportable(usageFetcher, Bundle.TypesPanel_usageLabel_title(), dataSource),
getStrExportable(osFetcher, Bundle.TypesPanel_osLabel_title(), dataSource),
new KeyValueItemExportable(Bundle.TypesPanel_sizeLabel_title(),
SizeRepresentationUtil.getBytesCell(getFetchResult(sizeFetcher, "Types", dataSource))),
typesChart,
getCountExportable(allocatedFetcher, Bundle.TypesPanel_filesByCategoryTable_allocatedRow_title(), dataSource),
getCountExportable(unallocatedFetcher, Bundle.TypesPanel_filesByCategoryTable_unallocatedRow_title(), dataSource),
getCountExportable(slackFetcher, Bundle.TypesPanel_filesByCategoryTable_slackRow_title(), dataSource),

View File

@ -0,0 +1,273 @@
/*
* 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.awt.Color;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xddf.usermodel.XDDFColor;
import org.apache.poi.xddf.usermodel.XDDFShapeProperties;
import org.apache.poi.xddf.usermodel.XDDFSolidFillProperties;
import org.apache.poi.xddf.usermodel.chart.AxisCrosses;
import org.apache.poi.xddf.usermodel.chart.AxisPosition;
import org.apache.poi.xddf.usermodel.chart.BarDirection;
import org.apache.poi.xddf.usermodel.chart.BarGrouping;
import org.apache.poi.xddf.usermodel.chart.ChartTypes;
import org.apache.poi.xddf.usermodel.chart.LegendPosition;
import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFCategoryAxis;
import org.apache.poi.xddf.usermodel.chart.XDDFChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend;
import org.apache.poi.xddf.usermodel.chart.XDDFDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory;
import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis;
import org.apache.poi.xssf.usermodel.XSSFChart;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet;
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;
/**
* Class that creates an excel stacked bar chart along with data table.
*/
public class BarChartExport implements ExcelItemExportable, ExcelSheetExport {
/**
* Creates an excel table model to be written to an excel sheet and used as
* a datasource for the chart.
*
* @param categories The categories with their data.
* @param keyColumnHeader The header column name for the table descriptions
* (i.e. types: file types / artifact types).
* @param chartTitle The title for the chart.
* @return An excel table export to be used as the data source for the chart
* in the excel document.
*/
private static ExcelTableExport<Pair<Object, List<Double>>, ? extends ExcelCellModel> getTableModel(
List<BarChartSeries> categories, String keyColumnHeader, String chartTitle) {
// get the row keys by finding the series with the largest set of bar items
// (they should all be equal, but just in case)
List<? extends Object> rowKeys = categories.stream()
.filter(cat -> cat != null && cat.getItems() != null)
.map(cat -> cat.getItems())
.max((items1, items2) -> Integer.compare(items1.size(), items2.size()))
.orElse(Collections.emptyList())
.stream()
.map((barChartItem) -> barChartItem.getKey())
.collect(Collectors.toList());
// map of (bar chart category index, bar chart item index) -> value
Map<Pair<Integer, Integer>, Double> valueMap = IntStream.range(0, categories.size())
.mapToObj(idx -> Pair.of(idx, categories.get(idx)))
.filter(pair -> pair.getValue() != null && pair.getValue().getItems() != null)
.flatMap(categoryPair -> {
return IntStream.range(0, categoryPair.getValue().getItems().size())
.mapToObj(idx -> Pair.of(idx, categoryPair.getValue().getItems().get(idx)))
.map(itemPair -> Pair.of(
Pair.of(categoryPair.getKey(), itemPair.getKey()),
itemPair.getValue() == null ? null : itemPair.getValue().getValue()));
})
.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue(), (v1, v2) -> v1));
// Create rows of data to be displayed where each row is a tuple of the bar chart item
// key and the list of values in category order.
List<Pair<Object, List<Double>>> values = IntStream.range(0, rowKeys.size())
.mapToObj(idx -> Pair.of(idx, rowKeys.get(idx)))
.map((rowPair) -> {
List<Double> items = IntStream.range(0, categories.size())
.mapToObj(idx -> valueMap.get(Pair.of(idx, rowPair.getKey())))
.collect(Collectors.toList());
return Pair.of(rowPair.getValue(), items);
})
.collect(Collectors.toList());
// Create the model for the category column
ColumnModel<Pair<Object, List<Double>>, DefaultCellModel<?>> categoryColumn
= new ColumnModel<>(keyColumnHeader, (row) -> new DefaultCellModel<>(row.getKey()));
// create the models for each category of data to be displayed
Stream<ColumnModel<Pair<Object, List<Double>>, DefaultCellModel<?>>> dataColumns = IntStream.range(0, categories.size())
.mapToObj(idx -> new ColumnModel<>(
categories.get(idx).getKey().toString(),
(row) -> new DefaultCellModel<>(row.getValue().get(idx))));
// create table
return new ExcelTableExport<Pair<Object, List<Double>>, DefaultCellModel<?>>(
chartTitle,
Stream.concat(Stream.of(categoryColumn), dataColumns)
.collect(Collectors.toList()),
values
);
}
private static final int DEFAULT_ROW_SIZE = 15;
private static final int DEFAULT_COL_SIZE = 10;
private static final int DEFAULT_ROW_PADDING = 1;
private static final int DEFAULT_COL_OFFSET = 1;
private final ExcelTableExport<Pair<Object, List<Double>>, ? extends ExcelCellModel> tableExport;
private final int colOffset;
private final int rowPadding;
private final int colSize;
private final int rowSize;
private final String chartTitle;
private final String sheetName;
private final List<BarChartSeries> categories;
private final String keyColumnHeader;
/**
* Main constructor that assumes some defaults (i.e. chart size follows
* defaults and sheet name is chart title).
*
* @param keyColumnHeader The header column name for the table descriptions
* (i.e. types: file types / artifact types).
* @param valueFormatString The excel format string to use for values.
* @param chartTitle The title for the chart.
* @param categories The categories along with data.
*/
public BarChartExport(String keyColumnHeader,
String valueFormatString,
String chartTitle,
List<BarChartSeries> categories) {
this(keyColumnHeader, valueFormatString, chartTitle, chartTitle, categories,
DEFAULT_COL_OFFSET, DEFAULT_ROW_PADDING, DEFAULT_COL_SIZE, DEFAULT_ROW_SIZE);
}
/**
* Main constructor.
*
* @param keyColumnHeader The header column name for the table descriptions
* (i.e. types: file types / artifact types).
* @param valueFormatString The excel format string to use for values.
* @param chartTitle The title for the chart.
* @param sheetName The sheet name if used as a sheet export.
* @param categories The categories along with data.
* @param colOffset The column spacing between the table and the chart.
* @param rowPadding The padding between this and data above or below (if
* used as an ExcelItemExportable).
* @param colSize The column size of the chart.
* @param rowSize The row size of the chart.
*/
public BarChartExport(String keyColumnHeader, String valueFormatString,
String chartTitle, String sheetName,
List<BarChartSeries> categories,
int colOffset, int rowPadding, int colSize, int rowSize) {
this.keyColumnHeader = keyColumnHeader;
this.tableExport = getTableModel(categories, keyColumnHeader, chartTitle);
this.colOffset = colOffset;
this.rowPadding = rowPadding;
this.colSize = colSize;
this.rowSize = rowSize;
this.chartTitle = chartTitle;
this.sheetName = sheetName;
this.categories = categories;
}
@Override
public String getSheetName() {
return sheetName;
}
@Override
public void renderSheet(Sheet sheet, ExcelExport.WorksheetEnv env) throws ExcelExport.ExcelExportException {
write(sheet, 0, 0, env);
}
@Override
public ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env) throws ExcelExportException {
if (!(sheet instanceof XSSFSheet)) {
throw new ExcelExportException("Sheet must be an XSSFSheet in order to write.");
}
XSSFSheet xssfSheet = (XSSFSheet) sheet;
// write pie chart table data
ItemDimensions tableDimensions = tableExport.write(xssfSheet, rowStart + rowPadding, colStart, env);
XSSFDrawing drawing = xssfSheet.createDrawingPatriarch();
int chartColStart = colStart + categories.size() + 1 + colOffset;
//createAnchor has arguments of (int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2);
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, chartColStart, rowStart + rowPadding, chartColStart + colSize + 1, rowStart + rowSize + 1);
XSSFChart chart = drawing.createChart(anchor);
chart.setTitleText(chartTitle);
chart.setTitleOverlay(false);
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.BOTTOM);
// Use a category axis for the bottom axis.
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
bottomAxis.setTitle(keyColumnHeader);
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
leftAxis.setVisible(false);
XDDFBarChartData data = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
data.setBarGrouping(BarGrouping.STACKED);
XDDFDataSource<String> headerSource = XDDFDataSourcesFactory.fromStringCellRange(xssfSheet,
new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(),
tableDimensions.getColStart(), tableDimensions.getColStart()));
data.setBarDirection(BarDirection.COL);
// set data for each series and set color if applicable
for (int i = 0; i < categories.size(); i++) {
XDDFChartData.Series series = data.addSeries(headerSource,
XDDFDataSourcesFactory.fromNumericCellRange(xssfSheet,
new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(),
tableDimensions.getColStart() + 1 + i, tableDimensions.getColStart() + 1 + i)));
series.setTitle(categories.size() > i && categories.get(i).getKey() != null ? categories.get(i).getKey().toString() : "", null);
if (categories.get(i).getColor() != null) {
Color color = categories.get(i).getColor();
byte[] colorArrARGB = ByteBuffer.allocate(4).putInt(color.getRGB()).array();
byte[] colorArrRGB = new byte[]{colorArrARGB[1], colorArrARGB[2], colorArrARGB[3]};
XDDFSolidFillProperties fill = new XDDFSolidFillProperties(XDDFColor.from(colorArrRGB));
XDDFShapeProperties properties = series.getShapeProperties();
if (properties == null) {
properties = new XDDFShapeProperties();
}
properties.setFillProperties(fill);
series.setShapeProperties(properties);
}
}
chart.plot(data);
return new ItemDimensions(rowStart, colStart, Math.max(tableDimensions.getRowEnd(), rowStart + rowSize) + rowPadding, chartColStart + colSize);
}
}

View File

@ -19,9 +19,7 @@
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.util.Collections;
import java.util.List;
import javax.swing.JLabel;
import org.apache.commons.collections4.CollectionUtils;
@ -34,90 +32,12 @@ import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.StandardBarPainter;
import org.jfree.data.category.DefaultCategoryDataset;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.BarChartItem;
/**
* A bar chart panel.
*/
public class BarChartPanel extends AbstractLoadableComponent<List<BarChartPanel.BarChartSeries>> {
/**
* Represents a series in a bar chart where all items pertain to one
* category.
*/
public static class BarChartSeries {
private final Comparable<?> key;
private final Color color;
private final List<BarChartItem> items;
/**
* Main constructor.
*
* @param key The key.
* @param color The color for this series.
* @param items The bars to be displayed for this series.
*/
public BarChartSeries(Comparable<?> key, Color color, List<BarChartItem> items) {
this.key = key;
this.color = color;
this.items = (items == null) ? Collections.emptyList() : Collections.unmodifiableList(items);
}
/**
* @return The color for this series.
*/
public Color getColor() {
return color;
}
/**
* @return The bars to be displayed for this series.
*/
public List<BarChartItem> getItems() {
return items;
}
/**
* @return The key for this item.
*/
public Comparable<?> getKey() {
return key;
}
}
/**
* An individual bar to be displayed in the bar chart.
*/
public static class BarChartItem {
private final Comparable<?> key;
private final double value;
/**
* Main constructor.
*
* @param key The key.
* @param value The value for this item.
*/
public BarChartItem(Comparable<?> key, double value) {
this.key = key;
this.value = value;
}
/**
* @return The key for this item.
*/
public Comparable<?> getKey() {
return key;
}
/**
* @return The value for this item.
*/
public double getValue() {
return value;
}
}
public class BarChartPanel extends AbstractLoadableComponent<List<BarChartSeries>> {
/**
* JFreeChart bar charts don't preserve the order of bars provided to the
@ -285,12 +205,12 @@ public class BarChartPanel extends AbstractLoadableComponent<List<BarChartPanel.
}
@Override
protected void setResults(List<BarChartPanel.BarChartSeries> data) {
protected void setResults(List<BarChartSeries> data) {
this.dataset.clear();
if (CollectionUtils.isNotEmpty(data)) {
for (int s = 0; s < data.size(); s++) {
BarChartPanel.BarChartSeries series = data.get(s);
BarChartSeries series = data.get(s);
if (series != null && CollectionUtils.isNotEmpty(series.getItems())) {
if (series.getColor() != null) {
this.plot.getRenderer().setSeriesPaint(s, series.getColor());

View File

@ -0,0 +1,101 @@
/*
* 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.awt.Color;
import java.util.Collections;
import java.util.List;
/**
* Represents a series in a bar chart where all items pertain to one category.
*/
public class BarChartSeries {
/**
* An individual bar to be displayed in the bar chart.
*/
public static class BarChartItem {
private final Comparable<?> key;
private final double value;
/**
* Main constructor.
*
* @param key The key.
* @param value The value for this item.
*/
public BarChartItem(Comparable<?> key, double value) {
this.key = key;
this.value = value;
}
/**
* @return The key for this item.
*/
public Comparable<?> getKey() {
return key;
}
/**
* @return The value for this item.
*/
public double getValue() {
return value;
}
}
private final Comparable<?> key;
private final Color color;
private final List<BarChartItem> items;
/**
* Main constructor.
*
* @param key The key.
* @param color The color for this series.
* @param items The bars to be displayed for this series.
*/
public BarChartSeries(Comparable<?> key, Color color, List<BarChartItem> items) {
this.key = key;
this.color = color;
this.items = (items == null) ? Collections.emptyList() : Collections.unmodifiableList(items);
}
/**
* @return The color for this series.
*/
public Color getColor() {
return color;
}
/**
* @return The bars to be displayed for this series.
*/
public List<BarChartItem> getItems() {
return items;
}
/**
* @return The key for this item.
*/
public Comparable<?> getKey() {
return key;
}
}

View File

@ -81,6 +81,8 @@ public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelSheet
sheet.autoSizeColumn(i);
}
// freeze header row
sheet.createFreezePane(0, 1);
}
@Override
@ -119,10 +121,8 @@ public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelSheet
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)
// Create Cell Style for each column (if one is needed)
for (int rowNum = 0; rowNum < safeData.size(); rowNum++) {
T rowData = safeData.get(rowNum);
Row row = sheet.createRow(rowNum + rowStart + 1);

View File

@ -352,6 +352,20 @@ public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
return this;
}
/**
* Returns the selected items or null if no item is selected.
* @return The selected items or null if no item is selected.
*/
public List<T> getSelectedItems() {
int selectedRow = this.table.getSelectedRow();
int count = this.table.getSelectedRowCount();
if (selectedRow < 0 || this.tableModel == null || selectedRow + count > this.tableModel.getDataRows().size()) {
return null;
} else {
return this.tableModel.getDataRows().subList(selectedRow, selectedRow + count);
}
}
@Override
protected synchronized void setResults(List<T> data) {
// get previously selected value

View File

@ -0,0 +1,195 @@
/*
* 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.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xddf.usermodel.chart.LegendPosition;
import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend;
import org.apache.poi.xddf.usermodel.chart.XDDFDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory;
import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFPieChartData;
import org.apache.poi.xssf.usermodel.XSSFChart;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieChart;
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;
/**
*
* Class that creates an excel pie chart along with data table.
*/
public class PieChartExport implements ExcelItemExportable, ExcelSheetExport {
private static final int DEFAULT_ROW_SIZE = 20;
private static final int DEFAULT_COL_SIZE = 10;
private static final int DEFAULT_ROW_PADDING = 1;
private static final int DEFAULT_COL_OFFSET = 1;
private final ExcelTableExport<PieChartItem, ? extends ExcelCellModel> tableExport;
private final int colOffset;
private final int rowPadding;
private final int colSize;
private final int rowSize;
private final String chartTitle;
private final String sheetName;
/**
* Main constructor assuming defaults.
*
* @param keyColumnHeader The header column name for the table descriptions
* (i.e. file types).
* @param valueColumnHeader The header column name for the values.
* @param valueFormatString The excel format string to use for values.
* @param chartTitle The title for the chart.
* @param slices The values for the pie slices.
*/
public PieChartExport(String keyColumnHeader,
String valueColumnHeader, String valueFormatString,
String chartTitle,
List<PieChartItem> slices) {
this(keyColumnHeader, valueColumnHeader, valueFormatString, chartTitle, chartTitle, slices,
DEFAULT_COL_OFFSET, DEFAULT_ROW_PADDING, DEFAULT_COL_SIZE, DEFAULT_ROW_SIZE);
}
/**
* Main constructor.
*
* @param keyColumnHeader The header column name for the table descriptions
* (i.e. file types).
* @param valueColumnHeader The header column name for the values.
* @param valueFormatString The excel format string to use for values.
* @param chartTitle The title for the chart.
* @param sheetName The sheet name if used as a sheet export.
* @param slices The values for the pie slices.
* @param colOffset The column spacing between the table and the chart.
* @param rowPadding The padding between this and data above or below (if
* used as an ExcelItemExportable).
* @param colSize The column size of the chart.
* @param rowSize The row size of the chart.
*/
public PieChartExport(String keyColumnHeader,
String valueColumnHeader, String valueFormatString,
String chartTitle, String sheetName,
List<PieChartItem> slices,
int colOffset, int rowPadding, int colSize, int rowSize) {
this.tableExport = new ExcelTableExport<>(chartTitle,
Arrays.asList(
new ColumnModel<>(keyColumnHeader, (slice) -> new DefaultCellModel<>(slice.getLabel())),
new ColumnModel<>(valueColumnHeader, (slice) -> new DefaultCellModel<>(slice.getValue(), null, valueFormatString))
),
slices);
this.colOffset = colOffset;
this.rowPadding = rowPadding;
this.colSize = colSize;
this.rowSize = rowSize;
this.chartTitle = chartTitle;
this.sheetName = sheetName;
}
@Override
public String getSheetName() {
return sheetName;
}
@Override
public void renderSheet(Sheet sheet, ExcelExport.WorksheetEnv env) throws ExcelExport.ExcelExportException {
write(sheet, 0, 0, env);
}
@Override
public ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env) throws ExcelExportException {
if (!(sheet instanceof XSSFSheet)) {
throw new ExcelExportException("Sheet must be an XSSFSheet in order to write.");
}
XSSFSheet xssfSheet = (XSSFSheet) sheet;
// write pie chart table data
ItemDimensions tableDimensions = tableExport.write(xssfSheet, rowStart + rowPadding, colStart, env);
XSSFDrawing drawing = xssfSheet.createDrawingPatriarch();
int chartColStart = colStart + 2 + colOffset;
//createAnchor has arguments of (int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2);
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, chartColStart, rowStart + rowPadding, chartColStart + colSize + 1, rowStart + rowSize + 1);
XSSFChart chart = drawing.createChart(anchor);
chart.setTitleText(chartTitle);
chart.setTitleOverlay(false);
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.RIGHT);
// CellRangeAddress has arguments of (int firstRow, int lastRow, int firstCol, int lastCol)
XDDFDataSource<String> cat = XDDFDataSourcesFactory.fromStringCellRange(xssfSheet,
new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(),
tableDimensions.getColStart(), tableDimensions.getColStart()));
XDDFNumericalDataSource<Double> val = XDDFDataSourcesFactory.fromNumericCellRange(xssfSheet,
new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(),
tableDimensions.getColStart() + 1, tableDimensions.getColStart() + 1));
// NOTE: There appears to be a classpath issue with POI (a version of 4.0.1 and 4.1.1 simultaneously)
// that is causing conflicts for XDDFPieChartData creation (i.e. the compiler thinks its using 4.1.1
// and the runtime thinks its using 4.0.1) Reflection is used below to use the 4.0.1 method while
// sidestepping compiler issues.
// XDDFPieChartData creation that can be used in poi >= 4.1.1:
// XDDFPieChartData data = (XDDFPieChartData) chart.createData(ChartTypes.PIE, bottomAxis, leftAxis);
// XDDFPieChartData creation that can be used in 4.0.1:
// XDDFPieChartData data = new XDDFPieChartData(chart.getCTChart().getPlotArea().addNewPieChart());
XDDFPieChartData data;
try {
Constructor<XDDFPieChartData> constructor = XDDFPieChartData.class.getConstructor(CTPieChart.class);
constructor.setAccessible(true);
data = constructor.newInstance(chart.getCTChart().getPlotArea().addNewPieChart());
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException | IllegalArgumentException ex) {
throw new ExcelExportException("Error while instantiating chart data.", ex);
}
data.setVaryColors(true);
data.addSeries(cat, val);
// Add data labels
if (!chart.getCTChart().getPlotArea().getPieChartArray(0).getSerArray(0).isSetDLbls()) {
chart.getCTChart().getPlotArea().getPieChartArray(0).getSerArray(0).addNewDLbls();
}
chart.getCTChart().getPlotArea().getPieChartArray(0).getSerArray(0).getDLbls().addNewShowVal().setVal(true);
chart.getCTChart().getPlotArea().getPieChartArray(0).getSerArray(0).getDLbls().addNewShowSerName().setVal(false);
chart.getCTChart().getPlotArea().getPieChartArray(0).getSerArray(0).getDLbls().addNewShowCatName().setVal(true);
chart.getCTChart().getPlotArea().getPieChartArray(0).getSerArray(0).getDLbls().addNewShowPercent().setVal(true);
chart.getCTChart().getPlotArea().getPieChartArray(0).getSerArray(0).getDLbls().addNewShowLegendKey().setVal(false);
chart.plot(data);
return new ItemDimensions(rowStart, colStart, Math.max(tableDimensions.getRowEnd(), rowStart + rowSize) + rowPadding, chartColStart + colSize);
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.awt.Color;
/**
* An individual pie chart slice in the pie chart.
*/
public class PieChartItem {
private final String label;
private final double value;
private final Color color;
/**
* Main constructor.
*
* @param label The label for this pie slice.
* @param value The value for this item.
* @param color The color for the pie slice. Can be null for
* auto-determined.
*/
public PieChartItem(String label, double value, Color color) {
this.label = label;
this.value = value;
this.color = color;
}
/**
* @return The label for this item.
*/
public String getLabel() {
return label;
}
/**
* @return The value for this item.
*/
public double getValue() {
return value;
}
/**
* @return The color for the pie slice or null for auto-determined.
*/
public Color getColor() {
return color;
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -39,52 +39,7 @@ import org.openide.util.NbBundle.Messages;
@Messages({
"PieChartPanel_noDataLabel=No Data"
})
public class PieChartPanel extends AbstractLoadableComponent<List<PieChartPanel.PieChartItem>> {
/**
* An individual pie chart slice in the pie chart.
*/
public static class PieChartItem {
private final String label;
private final double value;
private final Color color;
/**
* Main constructor.
*
* @param label The label for this pie slice.
* @param value The value for this item.
* @param color The color for the pie slice. Can be null for
* auto-determined.
*/
public PieChartItem(String label, double value, Color color) {
this.label = label;
this.value = value;
this.color = color;
}
/**
* @return The label for this item.
*/
public String getLabel() {
return label;
}
/**
* @return The value for this item.
*/
public double getValue() {
return value;
}
/**
* @return The color for the pie slice or null for auto-determined.
*/
public Color getColor() {
return color;
}
}
public class PieChartPanel extends AbstractLoadableComponent<List<PieChartItem>> {
private static final long serialVersionUID = 1L;
@ -176,12 +131,12 @@ public class PieChartPanel extends AbstractLoadableComponent<List<PieChartPanel.
}
@Override
protected void setResults(List<PieChartPanel.PieChartItem> data) {
protected void setResults(List<PieChartItem> data) {
this.dataset.clear();
this.plot.clearSectionPaints(false);
if (data != null && !data.isEmpty()) {
for (PieChartPanel.PieChartItem slice : data) {
for (PieChartItem slice : data) {
this.dataset.setValue(slice.getLabel(), slice.getValue());
if (slice.getColor() != null) {
this.plot.setSectionPaint(slice.getLabel(), slice.getColor());
@ -202,7 +157,7 @@ public class PieChartPanel extends AbstractLoadableComponent<List<PieChartPanel.
* @param data The data.
* @param message The message.
*/
public synchronized void showDataWithMessage(List<PieChartPanel.PieChartItem> data, String message) {
public synchronized void showDataWithMessage(List<PieChartItem> data, String message) {
setResults(data);
setMessage(true, message);
repaint();

View File

@ -59,6 +59,7 @@ SelectionContext.views=Views
ViewContextAction.errorMessage.cannotFindDirectory=Failed to locate directory.
ViewContextAction.errorMessage.cannotFindNode=Failed to locate data source node in tree.
ViewContextAction.errorMessage.cannotSelectDirectory=Failed to select directory in tree.
ViewContextAction.errorMessage.unsupportedParent=Unable to navigate to content not supported in this release.
VolumeDetailsPanel.volumeIDLabel.text=Volume ID:
VolumeDetailsPanel.volumeIDValue.text=...
VolumeDetailsPanel.startValue.text=...

View File

@ -304,7 +304,6 @@ public class DataResultFilterNode extends FilterNode {
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewFileInDir.text"), c));
}
// action to go to the source file of the artifact
// action to go to the source file of the artifact
Content fileContent = ban.getLookup().lookup(AbstractFile.class);
if (fileContent == null) {
Content content = ban.getLookup().lookup(Content.class);

View File

@ -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();
}
/**
@ -1078,6 +1116,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
* DataSourcesByType looking for the Results Node.
*
* @param node The node.
* @param dataSourceId The data source id.
* @return The child nodes that are at the data source level.
*/
private Node getResultsNodeSearch(Node node, long dataSourceId) {

View File

@ -63,6 +63,7 @@ import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskDataException;
import org.sleuthkit.datamodel.UnsupportedContent;
import org.sleuthkit.datamodel.VolumeSystem;
/**
@ -161,7 +162,8 @@ public class ViewContextAction extends AbstractAction {
@Messages({
"ViewContextAction.errorMessage.cannotFindDirectory=Failed to locate directory.",
"ViewContextAction.errorMessage.cannotSelectDirectory=Failed to select directory in tree.",
"ViewContextAction.errorMessage.cannotFindNode=Failed to locate data source node in tree."
"ViewContextAction.errorMessage.cannotFindNode=Failed to locate data source node in tree.",
"ViewContextAction.errorMessage.unsupportedParent=Unable to navigate to content not supported in this release."
})
public void actionPerformed(ActionEvent event) {
EventQueue.invokeLater(() -> {
@ -182,6 +184,13 @@ public class ViewContextAction extends AbstractAction {
return;
}
if ((parentContent != null)
&& (parentContent instanceof UnsupportedContent)) {
MessageNotifyUtil.Message.error(Bundle.ViewContextAction_errorMessage_unsupportedParent());
logger.log(Level.WARNING, String.format("Could not navigate to unsupported content with id: %d", parentContent.getId())); //NON-NLS
return;
}
/*
* Get the "Data Sources" node from the tree view.
*/

View File

@ -20,9 +20,9 @@ package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.event.ListSelectionListener;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
@ -43,7 +43,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
* unchanged or that there are no items to select.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract void configurePanel(boolean selected, int[] indicesSelected);
abstract void configurePanel(boolean selected, List<?> selectedItems);
/**
* Get the checkbox which enables and disables this filter.
@ -54,14 +54,12 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
abstract JCheckBox getCheckbox();
/**
* Get the list of values associated with this filter if one exists. If one
* does not exist this should return null.
* Add a list selection listener to the filter list in this panel
*
* @return The JList which contains the values available for selection for
* this filter.
* @param listener The list selection listener to add.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract JList<?> getList();
abstract void addListSelectionListener(ListSelectionListener listener);
/**
* Get any additional text that should be displayed under the checkbox. If
@ -93,8 +91,8 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
if (getCheckbox() != null) {
getCheckbox().addActionListener(actionListener);
}
if (getList() != null) {
getList().addListSelectionListener(listListener);
if (hasPanel() == true) {
addListSelectionListener(listListener);
}
}
@ -108,21 +106,9 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
abstract AbstractFilter getFilter();
/**
* Remove listeners from the checkbox and the list if they exist.
* Get whether or not the filter has sufficient options to be used.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void removeListeners() {
if (getCheckbox() != null) {
for (ActionListener listener : getCheckbox().getActionListeners()) {
getCheckbox().removeActionListener(listener);
}
}
if (getList() != null) {
for (ListSelectionListener listener : getList().getListSelectionListeners()) {
getList().removeListSelectionListener(listener);
}
}
}
abstract boolean isFilterSupported();
/**
* Return whether or not this filter has a panel.

View File

@ -29,6 +29,7 @@ import java.util.ArrayList;
import java.util.List;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.apache.commons.lang3.StringUtils;
@ -92,7 +93,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* @param column The column to add the DiscoveryFilterPanel to.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
final void addFilter(AbstractDiscoveryFilterPanel filterPanel, boolean isSelected, int[] indicesSelected, int column) {
final void addFilter(AbstractDiscoveryFilterPanel filterPanel, boolean isSelected, List<?> selectedItems, int column) {
if (!isInitialized) {
constraints.gridy = 0;
constraints.anchor = GridBagConstraints.FIRST_LINE_START;
@ -105,7 +106,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
constraints.gridy = secondColumnY;
}
constraints.gridx = 0;
filterPanel.configurePanel(isSelected, indicesSelected);
filterPanel.configurePanel(isSelected, selectedItems);
filterPanel.addListeners(this, this);
filters.add(filterPanel);
constraints.fill = GridBagConstraints.VERTICAL;
@ -149,17 +150,6 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
repaint();
}
/**
* Clear the filters from the panel
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
final void clearFilters() {
for (AbstractDiscoveryFilterPanel filterPanel : filters) {
filterPanel.removeListeners();
}
filters.clear();
}
/**
* Update the constraints and add a component to one of the columns.
*
@ -214,10 +204,16 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public void actionPerformed(ActionEvent e) {
//invoke it after all the currently queued gui actions are performed
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
validateFields();
validate();
repaint();
}
});
}
/**
* Is the Objects Detected Filter Supported.
@ -228,7 +224,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
boolean isObjectsFilterSupported() {
for (AbstractDiscoveryFilterPanel filter : filters) {
if (filter instanceof ObjectDetectedFilterPanel) {
return filter.getList().getModel().getSize() > 0;
return filter.isFilterSupported();
}
}
return false;
@ -243,7 +239,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
boolean isHashSetFilterSupported() {
for (AbstractDiscoveryFilterPanel filter : filters) {
if (filter instanceof HashSetFilterPanel) {
return filter.getList().getModel().getSize() > 0;
return filter.isFilterSupported();
}
}
return false;
@ -258,7 +254,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
boolean isInterestingItemsFilterSupported() {
for (AbstractDiscoveryFilterPanel filter : filters) {
if (filter instanceof InterestingItemsFilterPanel) {
return filter.getList().getModel().getSize() > 0;
return filter.isFilterSupported();
}
}
return false;
@ -290,10 +286,16 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
@Override
public void valueChanged(ListSelectionEvent evt) {
if (!evt.getValueIsAdjusting()) {
//invoke it after all the currently queued gui actions are performed
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
validateFields();
validate();
repaint();
}
});
}
}
/**

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.discovery.ui;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Arrays;
@ -113,19 +114,31 @@ class ArtifactMenuMouseAdapter extends java.awt.event.MouseAdapter {
*
* @throws TskCoreException
*/
@NbBundle.Messages({"ArtifactMenuMouseAdapter.noFile.text=File does not exist."})
private JMenuItem[] getMenuItems(BlackboardArtifact artifact) throws TskCoreException {
List<JMenuItem> menuItems = new ArrayList<>();
BlackboardAttribute pathIdAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
long contentId;
Long contentId;
if (pathIdAttr != null) {
contentId = pathIdAttr.getValueLong();
} else {
} else if (artifact.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() && artifact.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()) {
contentId = artifact.getObjectID();
} else {
contentId = null;
JMenuItem noFile = new JMenuItem();
noFile.setText(Bundle.ArtifactMenuMouseAdapter_noFile_text());
noFile.setEnabled(false);
noFile.setForeground(Color.RED);
menuItems.add(noFile);
}
Content content = artifact.getSleuthkitCase().getContentById(contentId);
menuItems.addAll(getTimelineMenuItems(artifact));
if (contentId != null) {
Content content = artifact.getSleuthkitCase().getContentById(contentId);
menuItems.addAll(getDataModelActionFactoryMenuItems(artifact, content));
menuItems.add(DeleteFileContentTagAction.getInstance().getMenuForFiles(Arrays.asList((AbstractFile) content)));
} else {
menuItems.add(AddBlackboardArtifactTagAction.getInstance().getMenuForContent(Arrays.asList(artifact)));
}
menuItems.add(DeleteFileBlackboardArtifactTagAction.getInstance().getMenuForArtifacts(Arrays.asList(artifact)));
return menuItems.toArray(new JMenuItem[0]);
}

View File

@ -28,42 +28,20 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,66,0,0,0,-65"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="artifactTypeScrollPane" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="artifactTypeScrollPane" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="artifactTypeScrollPane">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 27]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="artifactList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DefaultListModel&lt;ArtifactTypeItem&gt;()" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Component class="org.sleuthkit.autopsy.guiutils.CheckBoxListPanel" name="artifactList">
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;ArtifactTypeItem&gt;"/>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;BlackboardArtifact.ARTIFACT_TYPE&gt;"/>
</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>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -21,10 +21,9 @@ package org.sleuthkit.autopsy.discovery.ui;
import java.util.ArrayList;
import java.util.List;
import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import javax.swing.DefaultListModel;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData;
@ -34,7 +33,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Filter for selection of a specific Artifact type to limit results to.
*/
class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
final class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
private static final long serialVersionUID = 1L;
@ -45,7 +44,6 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
ArtifactTypeFilterPanel() {
initComponents();
setUpArtifactTypeFilter();
}
/**
@ -53,12 +51,9 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpArtifactTypeFilter() {
int count = 0;
DefaultListModel<ArtifactTypeItem> artifactTypeModel = (DefaultListModel<ArtifactTypeItem>) artifactList.getModel();
artifactTypeModel.removeAllElements();
artifactList.clearList();
for (BlackboardArtifact.ARTIFACT_TYPE artifactType : SearchData.Type.DOMAIN.getArtifactTypes()) {
artifactTypeModel.add(count, new ArtifactTypeItem(artifactType));
count++;
artifactList.addElement(artifactType.getDisplayName(), null, artifactType);
}
}
@ -67,13 +62,11 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
artifactTypeCheckbox = new javax.swing.JCheckBox();
artifactTypeScrollPane = new javax.swing.JScrollPane();
artifactList = new javax.swing.JList<>();
artifactList = new org.sleuthkit.autopsy.guiutils.CheckBoxListPanel<>();
org.openide.awt.Mnemonics.setLocalizedText(artifactTypeCheckbox, org.openide.util.NbBundle.getMessage(ArtifactTypeFilterPanel.class, "ArtifactTypeFilterPanel.artifactTypeCheckbox.text")); // NOI18N
artifactTypeCheckbox.addActionListener(new java.awt.event.ActionListener() {
@ -83,42 +76,32 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
});
setPreferredSize(new java.awt.Dimension(27, 27));
artifactTypeScrollPane.setPreferredSize(new java.awt.Dimension(27, 27));
artifactList.setModel(new DefaultListModel<ArtifactTypeItem>());
artifactList.setEnabled(false);
artifactTypeScrollPane.setViewportView(artifactList);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(artifactTypeScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(artifactTypeScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
setLayout(new java.awt.BorderLayout());
add(artifactList, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
private void artifactTypeCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_artifactTypeCheckboxActionPerformed
artifactTypeScrollPane.setEnabled(artifactTypeCheckbox.isSelected());
artifactList.setEnabled(artifactTypeCheckbox.isSelected());
}//GEN-LAST:event_artifactTypeCheckboxActionPerformed
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
void configurePanel(boolean selected, List<?> selectedItems) {
artifactTypeCheckbox.setSelected(selected);
if (artifactTypeCheckbox.isEnabled() && artifactTypeCheckbox.isSelected()) {
artifactTypeScrollPane.setEnabled(true);
artifactList.setEnabled(true);
if (indicesSelected != null) {
artifactList.setSelectedIndices(indicesSelected);
if (selectedItems != null) {
List<BlackboardArtifact.ARTIFACT_TYPE> artTypeList = new ArrayList<>();
for (Object item : selectedItems) {
if (item instanceof BlackboardArtifact.ARTIFACT_TYPE) {
artTypeList.add((BlackboardArtifact.ARTIFACT_TYPE) item);
}
}
if (!artTypeList.isEmpty()) {
artifactList.setSelectedElements(artTypeList);
}
}
} else {
artifactTypeScrollPane.setEnabled(false);
artifactList.setEnabled(false);
}
}
@ -129,12 +112,6 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
return artifactTypeCheckbox;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return artifactList;
}
@Override
JLabel getAdditionalLabel() {
return null;
@ -144,7 +121,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
@NbBundle.Messages({"ArtifactTypeFilterPanel.selectionNeeded.text=At least one Result type must be selected."})
@Override
String checkForError() {
if (artifactTypeCheckbox.isSelected() && artifactList.getSelectedValuesList().isEmpty()) {
if (artifactTypeCheckbox.isSelected() && artifactList.getSelectedElements().isEmpty()) {
return Bundle.ArtifactTypeFilterPanel_selectionNeeded_text();
}
return "";
@ -153,51 +130,26 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (artifactTypeCheckbox.isSelected() && !artifactList.getSelectedValuesList().isEmpty()) {
List<BlackboardArtifact.ARTIFACT_TYPE> artifactTypeList = new ArrayList<>();
for (ArtifactTypeItem item : artifactList.getSelectedValuesList()) {
artifactTypeList.add(item.getArtifactType());
}
if (artifactTypeCheckbox.isSelected() && isFilterSupported()) {
List<BlackboardArtifact.ARTIFACT_TYPE> artifactTypeList = artifactList.getSelectedElements();
return new ArtifactTypeFilter(artifactTypeList);
}
return null;
}
/**
* Utility class to allow us to display the AritfactType display name
* instead of the name.
*/
private class ArtifactTypeItem {
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
/**
* Construct a new ArtifactTypeItem.
*
* @param ds The artifact type being wrapped.
*/
ArtifactTypeItem(BlackboardArtifact.ARTIFACT_TYPE artifactType) {
this.artifactType = artifactType;
}
/**
* Get the ArtifactType represented by this ArtifactTypeItem.
*
* @return The ArtifactType represented by this ArtifactTypeItem.
*/
BlackboardArtifact.ARTIFACT_TYPE getArtifactType() {
return artifactType;
@Override
void addListSelectionListener(ListSelectionListener listener) {
artifactList.addListSelectionListener(listener);
}
@Override
public String toString() {
return artifactType.getDisplayName();
}
boolean isFilterSupported() {
return !artifactList.isEmpty();
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JList<ArtifactTypeItem> artifactList;
private org.sleuthkit.autopsy.guiutils.CheckBoxListPanel<BlackboardArtifact.ARTIFACT_TYPE> artifactList;
private javax.swing.JCheckBox artifactTypeCheckbox;
private javax.swing.JScrollPane artifactTypeScrollPane;
// End of variables declaration//GEN-END:variables
}

View File

@ -1,3 +1,4 @@
ArtifactMenuMouseAdapter.noFile.text=File does not exist.
ArtifactMenuMouseAdapter_ExternalViewer_label=Open in external viewer
ArtifactMenuMouseAdapter_label=Extract Files
ArtifactsListPanel.dateColumn.name=Date/Time

View File

@ -18,11 +18,15 @@
*/
package org.sleuthkit.autopsy.discovery.ui;
import java.util.logging.Level;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.corecomponents.DataContentPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Details panel for displaying the collection of content viewers.
@ -31,6 +35,7 @@ final class ContentViewerDetailsPanel extends AbstractArtifactDetailsPanel {
private static final long serialVersionUID = 1L;
private final DataContentPanel contentViewer = DataContentPanel.createInstance();
private final static Logger logger = Logger.getLogger(ContentViewerDetailsPanel.class.getName());
/**
* Creates new form ContentViewerDetailsPanel
@ -61,8 +66,18 @@ final class ContentViewerDetailsPanel extends AbstractArtifactDetailsPanel {
if (artifact != null) {
boolean useAssociatedFile = artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()
|| artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID();
BlackboardAttribute pathIdAttr = null;
if (useAssociatedFile) {
try {
pathIdAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error getting Path ID Attribute for artifact with ID: " + artifact.getArtifactID(), ex);
}
}
if (!useAssociatedFile || pathIdAttr != null) {
node = new BlackboardArtifactNode(artifact, useAssociatedFile);
}
}
contentViewer.setNode(node);
}

View File

@ -27,7 +27,7 @@
<Dimension value="[250, 30]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[250, 30]"/>
<Dimension value="[250, 50]"/>
</Property>
<Property name="requestFocusEnabled" type="boolean" value="false"/>
</Properties>
@ -41,49 +41,20 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="dataSourceScrollPane" alignment="0" pref="300" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="dataSourceScrollPane" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="dataSourceScrollPane">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 27]"/>
</Property>
</Properties>
<Component class="org.sleuthkit.autopsy.guiutils.CheckBoxListPanel" name="dataSourceCheckBoxList">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="dataSourceList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DefaultListModel&lt;DataSourceItem&gt;()" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
<Property name="visibleRowCount" type="int" value="5"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;DataSourceItem&gt;"/>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;DataSource&gt;"/>
</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>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -18,15 +18,14 @@
*/
package org.sleuthkit.autopsy.discovery.ui;
import java.util.ArrayList;
import java.util.Collections;
import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import java.util.List;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.DefaultListModel;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
@ -50,6 +49,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
DataSourceFilterPanel() {
initComponents();
setUpDataSourceFilter();
this.add(dataSourceCheckBoxList);
}
/**
@ -57,13 +57,11 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
dataSourceCheckbox = new javax.swing.JCheckBox();
dataSourceScrollPane = new javax.swing.JScrollPane();
dataSourceList = new javax.swing.JList<>();
dataSourceCheckBoxList = new org.sleuthkit.autopsy.guiutils.CheckBoxListPanel<>();
org.openide.awt.Mnemonics.setLocalizedText(dataSourceCheckbox, org.openide.util.NbBundle.getMessage(DataSourceFilterPanel.class, "DataSourceFilterPanel.dataSourceCheckbox.text")); // NOI18N
dataSourceCheckbox.setMaximumSize(new java.awt.Dimension(150, 25));
@ -76,54 +74,41 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
});
setMinimumSize(new java.awt.Dimension(250, 30));
setPreferredSize(new java.awt.Dimension(250, 30));
setPreferredSize(new java.awt.Dimension(250, 50));
setRequestFocusEnabled(false);
dataSourceScrollPane.setPreferredSize(new java.awt.Dimension(27, 27));
dataSourceList.setModel(new DefaultListModel<DataSourceItem>());
dataSourceList.setEnabled(false);
dataSourceList.setVisibleRowCount(5);
dataSourceScrollPane.setViewportView(dataSourceList);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(dataSourceScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(dataSourceScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(0, 0, 0))
);
setLayout(new java.awt.BorderLayout());
add(dataSourceCheckBoxList, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
private void dataSourceCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dataSourceCheckboxActionPerformed
dataSourceList.setEnabled(dataSourceCheckbox.isSelected());
dataSourceCheckBoxList.setEnabled(dataSourceCheckbox.isSelected());
}//GEN-LAST:event_dataSourceCheckboxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.guiutils.CheckBoxListPanel<DataSource> dataSourceCheckBoxList;
private javax.swing.JCheckBox dataSourceCheckbox;
private javax.swing.JList<DataSourceItem> dataSourceList;
private javax.swing.JScrollPane dataSourceScrollPane;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
void configurePanel(boolean selected, List<?> selectedItems) {
dataSourceCheckbox.setSelected(selected);
if (dataSourceCheckbox.isEnabled() && dataSourceCheckbox.isSelected()) {
dataSourceScrollPane.setEnabled(true);
dataSourceList.setEnabled(true);
if (indicesSelected != null) {
dataSourceList.setSelectedIndices(indicesSelected);
dataSourceCheckBoxList.setEnabled(true);
if (selectedItems != null) {
List<DataSource> dsList = new ArrayList<>();
for (Object item : selectedItems) {
if (item instanceof DataSource) {
dsList.add((DataSource) item);
}
}
if (!dsList.isEmpty()) {
dataSourceCheckBoxList.setSelectedElements(dsList);
}
}
} else {
dataSourceScrollPane.setEnabled(false);
dataSourceList.setEnabled(false);
dataSourceCheckBoxList.setEnabled(false);
}
}
@ -143,67 +128,36 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpDataSourceFilter() {
int count = 0;
try {
DefaultListModel<DataSourceItem> dsListModel = (DefaultListModel<DataSourceItem>) dataSourceList.getModel();
dsListModel.removeAllElements();
dataSourceCheckBoxList.clearList();
List<DataSource> dataSources = Case.getCurrentCase().getSleuthkitCase().getDataSources();
Collections.sort(dataSources, (DataSource ds1, DataSource ds2) -> ds1.getName().compareToIgnoreCase(ds2.getName()));
for (DataSource ds : dataSources) {
dsListModel.add(count, new DataSourceItem(ds));
count++;
dataSourceCheckBoxList.addElement(ds.getName() + " (ID: " + ds.getId() + ")", null, ds);
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error loading data sources", ex);
dataSourceCheckbox.setEnabled(false);
dataSourceList.setEnabled(false);
dataSourceCheckBoxList.setEnabled(false);
}
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return dataSourceList;
}
/**
* Utility class to allow us to display the data source ID along with the
* name
*/
private class DataSourceItem {
private final DataSource ds;
/**
* Construct a new DataSourceItem.
*
* @param ds The data source being wrapped.
*/
DataSourceItem(DataSource ds) {
this.ds = ds;
}
/**
* Get the data source represented by this data source item.
*
* @return The data source represented by this data source item.
*/
DataSource getDataSource() {
return ds;
}
@Override
public String toString() {
return ds.getName() + " (ID: " + ds.getId() + ")";
void addListSelectionListener(ListSelectionListener listener) {
dataSourceCheckBoxList.addListSelectionListener(listener);
}
@Override
boolean isFilterSupported() {
return !dataSourceCheckBoxList.isEmpty();
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"DataSourceFilterPanel.error.text=At least one data source must be selected."})
@Override
String checkForError() {
if (dataSourceCheckbox.isSelected() && dataSourceList.getSelectedValuesList().isEmpty()) {
if (dataSourceCheckbox.isSelected() && dataSourceCheckBoxList.getSelectedElements().isEmpty()) {
return Bundle.DataSourceFilterPanel_error_text();
}
return "";
@ -213,9 +167,10 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
@Override
AbstractFilter getFilter() {
if (dataSourceCheckbox.isSelected()) {
List<DataSource> dataSources = dataSourceList.getSelectedValuesList().stream().map(t -> t.getDataSource()).collect(Collectors.toList());
List<DataSource> dataSources = dataSourceCheckBoxList.getSelectedElements();
return new SearchFiltering.DataSourceFilter(dataSources);
}
return null;
}
}

View File

@ -25,11 +25,11 @@ import java.awt.event.ActionListener;
import java.time.LocalDate;
import java.time.Period;
import java.time.ZoneId;
import java.util.List;
import java.util.logging.Level;
import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JSpinner;
import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle;
@ -38,7 +38,6 @@ import org.sleuthkit.autopsy.communications.Utils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
import org.sleuthkit.datamodel.TimelineManager;
import org.sleuthkit.datamodel.TskCoreException;
/**
@ -71,7 +70,6 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
@ -235,7 +233,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
void configurePanel(boolean selected, List<?> selectedItems) {
dateFilterCheckBox.setSelected(selected);
if (dateFilterCheckBox.isEnabled() && dateFilterCheckBox.isSelected()) {
mostRecentRadioButton.setEnabled(true);
@ -253,11 +251,6 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
return dateFilterCheckBox;
}
@Override
JList<?> getList() {
return null;
}
@Override
JLabel getAdditionalLabel() {
return null;
@ -285,35 +278,6 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
});
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void removeListeners() {
for (ActionListener listener : dateFilterCheckBox.getActionListeners()) {
dateFilterCheckBox.removeActionListener(listener);
}
for (ActionListener listener : rangeRadioButton.getActionListeners()) {
rangeRadioButton.removeActionListener(listener);
}
for (ActionListener listener : mostRecentRadioButton.getActionListeners()) {
mostRecentRadioButton.removeActionListener(listener);
}
for (ActionListener listener : rangeRadioButton.getActionListeners()) {
rangeRadioButton.removeActionListener(listener);
}
for (ActionListener listener : startCheckBox.getActionListeners()) {
startCheckBox.removeActionListener(listener);
}
for (ActionListener listener : endCheckBox.getActionListeners()) {
endCheckBox.removeActionListener(listener);
}
for (DateChangeListener listener : endDatePicker.getDateChangeListeners()) {
endDatePicker.removeDateChangeListener(listener);
}
for (DateChangeListener listener : startDatePicker.getDateChangeListeners()) {
startDatePicker.removeDateChangeListener(listener);
}
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"DateFilterPanel.invalidRange.text=Range or Only Last must be selected.",
"DateFilterPanel.startOrEndNeeded.text=A start or end date must be specified to use the range filter.",
@ -372,4 +336,15 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JCheckBox startCheckBox;
private com.github.lgooddatepicker.components.DatePicker startDatePicker;
// End of variables declaration//GEN-END:variables
@Override
void addListSelectionListener(ListSelectionListener listener) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
boolean isFilterSupported() {
return true;
}
}

View File

@ -6,6 +6,9 @@
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[600, 300]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[1000, 800]"/>
</Property>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
@ -21,7 +24,7 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-35,0,0,2,-11"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-54,0,0,2,-11"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
@ -67,19 +70,19 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="filler1" min="-2" pref="10" max="-2" attributes="0"/>
<Component id="step1Label" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="videosButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="imagesButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="documentsButton" alignment="3" min="-2" pref="43" max="-2" attributes="0"/>
<Component id="domainsButton" alignment="3" min="-2" pref="43" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -244,14 +247,14 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<Component id="sortingPanel" min="-2" pref="89" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Component id="sortingPanel" min="-2" pref="82" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="errorLabel" alignment="0" min="-2" pref="23" max="-2" attributes="0"/>
<Component id="searchButton" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -317,19 +320,19 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="groupByCombobox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="groupByLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="orderByCombobox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="orderByLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="groupSortingComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="orderGroupsByLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>

View File

@ -140,7 +140,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
});
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, this.new CasePropertyChangeListener());
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, this.new ModuleChangeListener());
setPreferredSize(new java.awt.Dimension(1000, 650));
setPreferredSize(new java.awt.Dimension(1000, 800));
}
/**
@ -319,6 +319,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setMinimumSize(new java.awt.Dimension(600, 300));
setPreferredSize(new java.awt.Dimension(1000, 800));
imagesButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/pictures-icon.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(imagesButton, org.openide.util.NbBundle.getMessage(DiscoveryDialog.class, "DiscoveryDialog.imagesButton.text")); // NOI18N
@ -402,17 +403,17 @@ final class DiscoveryDialog extends javax.swing.JDialog {
toolBarPanelLayout.setVerticalGroup(
toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(toolBarPanelLayout.createSequentialGroup()
.addGap(8, 8, 8)
.addGap(6, 6, 6)
.addGroup(toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(step1Label))
.addGap(8, 8, 8)
.addGap(6, 6, 6)
.addGroup(toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(videosButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(imagesButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(documentsButton, javax.swing.GroupLayout.PREFERRED_SIZE, 43, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(domainsButton, javax.swing.GroupLayout.PREFERRED_SIZE, 43, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(8, 8, 8))
.addGap(6, 6, 6))
);
getContentPane().add(toolBarPanel, java.awt.BorderLayout.PAGE_START);
@ -457,17 +458,17 @@ final class DiscoveryDialog extends javax.swing.JDialog {
sortingPanelLayout.setVerticalGroup(
sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(sortingPanelLayout.createSequentialGroup()
.addGap(8, 8, 8)
.addGap(6, 6, 6)
.addGroup(sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(groupByCombobox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(groupByLabel)
.addComponent(orderByCombobox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(orderByLabel))
.addGap(8, 8, 8)
.addGap(6, 6, 6)
.addGroup(sortingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(groupSortingComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(orderGroupsByLabel))
.addGap(8, 8, 8))
.addGap(6, 6, 6))
);
javax.swing.GroupLayout displaySettingsPanelLayout = new javax.swing.GroupLayout(displaySettingsPanel);
@ -487,13 +488,13 @@ final class DiscoveryDialog extends javax.swing.JDialog {
displaySettingsPanelLayout.setVerticalGroup(
displaySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, displaySettingsPanelLayout.createSequentialGroup()
.addGap(8, 8, 8)
.addComponent(sortingPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 89, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(8, 8, 8)
.addGap(6, 6, 6)
.addComponent(sortingPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 82, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(6, 6, 6)
.addGroup(displaySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(errorLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(searchButton))
.addGap(8, 8, 8))
.addGap(6, 6, 6))
);
getContentPane().add(displaySettingsPanel, java.awt.BorderLayout.PAGE_END);

View File

@ -18,6 +18,9 @@
*/
package org.sleuthkit.autopsy.discovery.ui;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData;
@ -38,16 +41,21 @@ final class DocumentFilterPanel extends AbstractFiltersPanel {
super();
initComponents();
SizeFilterPanel sizeFilterPanel = new SizeFilterPanel(TYPE);
int[] sizeIndicesSelected = {3, 4, 5};
addFilter(sizeFilterPanel, true, sizeIndicesSelected, 0);
List<SearchData.FileSize> defaultSizes = new ArrayList<>();
defaultSizes.add(SearchData.FileSize.LARGE_IMAGE);
defaultSizes.add(SearchData.FileSize.XLARGE_IMAGE);
defaultSizes.add(SearchData.FileSize.XXLARGE_IMAGE);
addFilter(sizeFilterPanel, true, defaultSizes, 0);
addFilter(new DataSourceFilterPanel(), false, null, 0);
int[] pastOccurrencesIndices;
List<SearchData.Frequency> defaultFrequencies = new ArrayList<>();
if (!CentralRepository.isEnabled()) {
pastOccurrencesIndices = new int[]{0};
defaultFrequencies.add(SearchData.Frequency.UNKNOWN);
} else {
pastOccurrencesIndices = new int[]{2, 3, 4};
defaultFrequencies.add(SearchData.Frequency.RARE);
defaultFrequencies.add(SearchData.Frequency.UNIQUE);
defaultFrequencies.add(SearchData.Frequency.COMMON);
}
addFilter(new PastOccurrencesFilterPanel(TYPE), true, pastOccurrencesIndices, 0);
addFilter(new PastOccurrencesFilterPanel(TYPE), true, defaultFrequencies, 0);
addFilter(new HashSetFilterPanel(), false, null, 1);
addFilter(new InterestingItemsFilterPanel(), false, null, 1);
addFilter(new ParentFolderFilterPanel(), false, null, 1);

View File

@ -18,7 +18,6 @@
*/
package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes;
import org.sleuthkit.autopsy.discovery.search.ResultsSorter;
@ -44,11 +43,7 @@ public class DomainFilterPanel extends AbstractFiltersPanel {
addFilter(new KnownAccountTypeFilterPanel(), false, null, 1);
addFilter(new ArtifactTypeFilterPanel(), false, null, 1);
addFilter(new DateFilterPanel(), false, null, 1);
int[] pastOccurrencesIndices = null;
if (CentralRepository.isEnabled()) {
pastOccurrencesIndices = new int[]{2, 3, 4};
}
addFilter(new PastOccurrencesFilterPanel(TYPE), true, pastOccurrencesIndices, 0);
addFilter(new PastOccurrencesFilterPanel(TYPE), false, null, 0);
addPanelsToScrollPane(domainFiltersSplitPane);
setLastGroupingAttributeType(DiscoveryAttributes.GroupingAttributeType.LAST_ACTIVITY_DATE);
setLastSortingMethod(ResultsSorter.SortingMethod.BY_DOMAIN_NAME);

View File

@ -42,39 +42,17 @@
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="hashSetScrollPane" alignment="0" pref="300" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="hashSetScrollPane" alignment="0" pref="60" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="hashSetScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="hashSetList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DefaultListModel&lt;String&gt;()" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
<Property name="visibleRowCount" type="int" value="3"/>
</Properties>
<Component class="org.sleuthkit.autopsy.guiutils.CheckBoxListPanel" name="hashSetList">
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</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>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -18,13 +18,13 @@
*/
package org.sleuthkit.autopsy.discovery.ui;
import java.util.ArrayList;
import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import java.util.List;
import java.util.logging.Level;
import javax.swing.DefaultListModel;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
@ -48,6 +48,7 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
HashSetFilterPanel() {
initComponents();
setUpHashFilter();
add(hashSetList);
}
/**
@ -55,15 +56,12 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpHashFilter() {
int count = 0;
try {
DefaultListModel<String> hashListModel = (DefaultListModel<String>) hashSetList.getModel();
hashListModel.removeAllElements();
hashSetList.clearList();
List<String> setNames = DiscoveryUiUtils.getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME);
for (String name : setNames) {
hashListModel.add(count, name);
count++;
hashSetList.addElement(name, null, name);
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error loading hash set names", ex);
@ -77,13 +75,11 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
hashSetCheckbox = new javax.swing.JCheckBox();
hashSetScrollPane = new javax.swing.JScrollPane();
hashSetList = new javax.swing.JList<>();
hashSetList = new org.sleuthkit.autopsy.guiutils.CheckBoxListPanel<>();
org.openide.awt.Mnemonics.setLocalizedText(hashSetCheckbox, org.openide.util.NbBundle.getMessage(HashSetFilterPanel.class, "HashSetFilterPanel.hashSetCheckbox.text")); // NOI18N
hashSetCheckbox.setMaximumSize(new java.awt.Dimension(150, 25));
@ -97,22 +93,8 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
setMinimumSize(new java.awt.Dimension(250, 30));
setPreferredSize(new java.awt.Dimension(250, 30));
hashSetList.setModel(new DefaultListModel<String>());
hashSetList.setEnabled(false);
hashSetList.setVisibleRowCount(3);
hashSetScrollPane.setViewportView(hashSetList);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(hashSetScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(hashSetScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 60, Short.MAX_VALUE)
);
setLayout(new java.awt.BorderLayout());
add(hashSetList, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
private void hashSetCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hashSetCheckboxActionPerformed
@ -122,24 +104,29 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox hashSetCheckbox;
private javax.swing.JList<String> hashSetList;
private javax.swing.JScrollPane hashSetScrollPane;
private org.sleuthkit.autopsy.guiutils.CheckBoxListPanel<String> hashSetList;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
boolean hasHashSets = hashSetList.getModel().getSize() > 0;
void configurePanel(boolean selected, List<?> selectedItems) {
boolean hasHashSets = isFilterSupported();
hashSetCheckbox.setEnabled(hasHashSets);
hashSetCheckbox.setSelected(selected && hasHashSets);
if (hashSetCheckbox.isEnabled() && hashSetCheckbox.isSelected()) {
hashSetScrollPane.setEnabled(true);
hashSetList.setEnabled(true);
if (indicesSelected != null) {
hashSetList.setSelectedIndices(indicesSelected);
if (selectedItems != null) {
List<String> setList = new ArrayList<>();
for (Object item : selectedItems) {
if (item instanceof String) {
setList.add((String) item);
}
}
if (!setList.isEmpty()) {
hashSetList.setSelectedElements(setList);
}
}
} else {
hashSetScrollPane.setEnabled(false);
hashSetList.setEnabled(false);
}
}
@ -159,24 +146,30 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
@NbBundle.Messages({"HashSetFilterPanel.error.text=At least one hash set name must be selected."})
@Override
String checkForError() {
if (hashSetCheckbox.isSelected() && hashSetList.getSelectedValuesList().isEmpty()) {
if (hashSetCheckbox.isSelected() && hashSetList.getSelectedElements().isEmpty()) {
return Bundle.HashSetFilterPanel_error_text();
}
return "";
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return hashSetList;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (hashSetCheckbox.isSelected()) {
return new SearchFiltering.HashSetFilter(hashSetList.getSelectedValuesList());
List<String> setList = hashSetList.getSelectedElements();
return new SearchFiltering.InterestingFileSetFilter(setList);
}
return null;
}
@Override
void addListSelectionListener(ListSelectionListener listener) {
hashSetList.addListSelectionListener(listener);
}
@Override
boolean isFilterSupported() {
return !hashSetList.isEmpty();
}
}

View File

@ -18,6 +18,8 @@
*/
package org.sleuthkit.autopsy.discovery.ui;
import java.util.ArrayList;
import java.util.List;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData;
@ -38,17 +40,22 @@ final class ImageFilterPanel extends AbstractFiltersPanel {
super();
initComponents();
SizeFilterPanel sizeFilterPanel = new SizeFilterPanel(TYPE);
int[] sizeIndicesSelected = {3, 4, 5};
addFilter(sizeFilterPanel, true, sizeIndicesSelected, 0);
List<SearchData.FileSize> defaultSizes = new ArrayList<>();
defaultSizes.add(SearchData.FileSize.LARGE_IMAGE);
defaultSizes.add(SearchData.FileSize.XLARGE_IMAGE);
defaultSizes.add(SearchData.FileSize.XXLARGE_IMAGE);
addFilter(sizeFilterPanel, true, defaultSizes, 0);
addFilter(new DataSourceFilterPanel(), false, null, 0);
int[] pastOccurrencesIndices;
List<SearchData.Frequency> defaultFrequencies = new ArrayList<>();
if (!CentralRepository.isEnabled()) {
pastOccurrencesIndices = new int[]{0};
defaultFrequencies.add(SearchData.Frequency.UNKNOWN);
} else {
pastOccurrencesIndices = new int[]{2, 3, 4};
defaultFrequencies.add(SearchData.Frequency.RARE);
defaultFrequencies.add(SearchData.Frequency.UNIQUE);
defaultFrequencies.add(SearchData.Frequency.COMMON);
}
addFilter(new PastOccurrencesFilterPanel(TYPE), true, pastOccurrencesIndices, 0);
addFilter(new UserCreatedFilterPanel(), false, null, 1);
addFilter(new PastOccurrencesFilterPanel(TYPE), true, defaultFrequencies, 0);
addFilter(new UserCreatedFilterPanel(), false, null, 0);
addFilter(new HashSetFilterPanel(), false, null, 1);
addFilter(new InterestingItemsFilterPanel(), false, null, 1);
addFilter(new ObjectDetectedFilterPanel(), false, null, 1);

View File

@ -40,46 +40,20 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="interestingItemsScrollPane" pref="300" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="interestingItemsScrollPane" alignment="0" pref="88" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="interestingItemsScrollPane">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 27]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="interestingItemsList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DefaultListModel&lt;String&gt;()" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
<Property name="visibleRowCount" type="int" value="2"/>
</Properties>
<Component class="org.sleuthkit.autopsy.guiutils.CheckBoxListPanel" name="interestingItemsList">
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</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>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -18,13 +18,13 @@
*/
package org.sleuthkit.autopsy.discovery.ui;
import java.util.ArrayList;
import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import java.util.List;
import java.util.logging.Level;
import javax.swing.DefaultListModel;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
@ -48,6 +48,7 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
InterestingItemsFilterPanel() {
initComponents();
setUpInterestingItemsFilter();
}
/**
@ -55,15 +56,12 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpInterestingItemsFilter() {
int count = 0;
try {
DefaultListModel<String> intListModel = (DefaultListModel<String>) interestingItemsList.getModel();
intListModel.removeAllElements();
interestingItemsList.clearList();
List<String> setNames = DiscoveryUiUtils.getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME);
for (String name : setNames) {
intListModel.add(count, name);
count++;
interestingItemsList.addElement(name, null, name);
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error loading interesting file set names", ex);
@ -77,13 +75,11 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
interestingItemsCheckbox = new javax.swing.JCheckBox();
interestingItemsScrollPane = new javax.swing.JScrollPane();
interestingItemsList = new javax.swing.JList<>();
interestingItemsList = new org.sleuthkit.autopsy.guiutils.CheckBoxListPanel<>();
org.openide.awt.Mnemonics.setLocalizedText(interestingItemsCheckbox, org.openide.util.NbBundle.getMessage(InterestingItemsFilterPanel.class, "InterestingItemsFilterPanel.interestingItemsCheckbox.text")); // NOI18N
interestingItemsCheckbox.setMaximumSize(new java.awt.Dimension(150, 25));
@ -97,24 +93,8 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
setMinimumSize(new java.awt.Dimension(250, 30));
setPreferredSize(new java.awt.Dimension(250, 30));
interestingItemsScrollPane.setPreferredSize(new java.awt.Dimension(27, 27));
interestingItemsList.setModel(new DefaultListModel<String>());
interestingItemsList.setEnabled(false);
interestingItemsList.setVisibleRowCount(2);
interestingItemsScrollPane.setViewportView(interestingItemsList);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(interestingItemsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(interestingItemsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 88, Short.MAX_VALUE)
);
setLayout(new java.awt.BorderLayout());
add(interestingItemsList, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
private void interestingItemsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interestingItemsCheckboxActionPerformed
@ -123,18 +103,24 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
boolean hasInterestingItems = interestingItemsList.getModel().getSize() > 0;
void configurePanel(boolean selected, List<?> selectedItems) {
boolean hasInterestingItems = isFilterSupported();
interestingItemsCheckbox.setEnabled(hasInterestingItems);
interestingItemsCheckbox.setSelected(selected && hasInterestingItems);
if (interestingItemsCheckbox.isEnabled() && interestingItemsCheckbox.isSelected()) {
interestingItemsScrollPane.setEnabled(true);
interestingItemsList.setEnabled(true);
if (indicesSelected != null) {
interestingItemsList.setSelectedIndices(indicesSelected);
if (selectedItems != null) {
List<String> intItemList = new ArrayList<>();
for (Object item : selectedItems) {
if (item instanceof String) {
intItemList.add((String) item);
}
}
if (!intItemList.isEmpty()) {
interestingItemsList.setSelectedElements(intItemList);
}
}
} else {
interestingItemsScrollPane.setEnabled(false);
interestingItemsList.setEnabled(false);
}
}
@ -154,7 +140,7 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
@NbBundle.Messages({"InterestingItemsFilterPanel.error.text=At least one interesting file set name must be selected."})
@Override
String checkForError() {
if (interestingItemsCheckbox.isSelected() && interestingItemsList.getSelectedValuesList().isEmpty()) {
if (interestingItemsCheckbox.isSelected() && interestingItemsList.getSelectedElements().isEmpty()) {
return Bundle.InterestingItemsFilterPanel_error_text();
}
return "";
@ -163,22 +149,26 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox interestingItemsCheckbox;
private javax.swing.JList<String> interestingItemsList;
private javax.swing.JScrollPane interestingItemsScrollPane;
private org.sleuthkit.autopsy.guiutils.CheckBoxListPanel<String> interestingItemsList;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return interestingItemsList;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (interestingItemsCheckbox.isSelected()) {
return new SearchFiltering.InterestingFileSetFilter(interestingItemsList.getSelectedValuesList());
List<String> itemsList = interestingItemsList.getSelectedElements();
return new SearchFiltering.InterestingFileSetFilter(itemsList);
}
return null;
}
@Override
void addListSelectionListener(ListSelectionListener listener) {
interestingItemsList.addListSelectionListener(listener);
}
@Override
boolean isFilterSupported() {
return !interestingItemsList.isEmpty();
}
}

View File

@ -19,14 +19,16 @@
package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import java.util.List;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.event.ListSelectionListener;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
/**
* Panel to allow filtering only domains with known account types (TSK_WEB_ACCOUNT_TYPE).
* Panel to allow filtering only domains with known account types
* (TSK_WEB_ACCOUNT_TYPE).
*/
final class KnownAccountTypeFilterPanel extends AbstractDiscoveryFilterPanel {
@ -72,7 +74,7 @@ final class KnownAccountTypeFilterPanel extends AbstractDiscoveryFilterPanel {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
void configurePanel(boolean selected, List<?> selectedItems) {
knownAccountType.setSelected(selected);
}
@ -97,11 +99,6 @@ final class KnownAccountTypeFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JCheckBox knownAccountType;
// End of variables declaration//GEN-END:variables
@Override
JList<?> getList() {
return null;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
@ -115,4 +112,14 @@ final class KnownAccountTypeFilterPanel extends AbstractDiscoveryFilterPanel {
boolean hasPanel() {
return false;
}
@Override
void addListSelectionListener(ListSelectionListener listener) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
boolean isFilterSupported() {
return true;
}
}

View File

@ -45,48 +45,17 @@
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="objectsScrollPane" alignment="0" pref="300" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="objectsScrollPane" pref="64" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="objectsScrollPane">
<Properties>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 27]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="objectsList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DefaultListModel&lt;String&gt;()" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 32767]"/>
</Property>
<Property name="visibleRowCount" type="int" value="2"/>
</Properties>
<Component class="org.sleuthkit.autopsy.guiutils.CheckBoxListPanel" name="objectsList">
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</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>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -1,7 +1,7 @@
/*
* Autopsy
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,13 +18,13 @@
*/
package org.sleuthkit.autopsy.discovery.ui;
import java.util.ArrayList;
import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import java.util.List;
import java.util.logging.Level;
import javax.swing.DefaultListModel;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
@ -48,6 +48,7 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
ObjectDetectedFilterPanel() {
initComponents();
setUpObjectFilter();
add(objectsList);
}
/**
@ -55,14 +56,11 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpObjectFilter() {
int count = 0;
try {
DefaultListModel<String> objListModel = (DefaultListModel<String>) objectsList.getModel();
objListModel.removeAllElements();
objectsList.clearList();
List<String> setNames = DiscoveryUiUtils.getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION);
for (String name : setNames) {
objListModel.add(count, name);
count++;
objectsList.addElement(name, null, name);
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error loading object detected set names", ex);
@ -81,8 +79,7 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
private void initComponents() {
objectsCheckbox = new javax.swing.JCheckBox();
objectsScrollPane = new javax.swing.JScrollPane();
objectsList = new javax.swing.JList<>();
objectsList = new org.sleuthkit.autopsy.guiutils.CheckBoxListPanel<>();
org.openide.awt.Mnemonics.setLocalizedText(objectsCheckbox, org.openide.util.NbBundle.getMessage(ObjectDetectedFilterPanel.class, "ObjectDetectedFilterPanel.text")); // NOI18N
objectsCheckbox.setMaximumSize(new java.awt.Dimension(150, 25));
@ -99,26 +96,8 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
setMinimumSize(new java.awt.Dimension(250, 30));
setPreferredSize(new java.awt.Dimension(250, 30));
objectsScrollPane.setName(""); // NOI18N
objectsScrollPane.setPreferredSize(new java.awt.Dimension(27, 27));
objectsList.setModel(new DefaultListModel<String>());
objectsList.setEnabled(false);
objectsList.setMaximumSize(new java.awt.Dimension(32767, 32767));
objectsList.setVisibleRowCount(2);
objectsScrollPane.setViewportView(objectsList);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(objectsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(objectsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 64, Short.MAX_VALUE)
);
setLayout(new java.awt.BorderLayout());
add(objectsList, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
private void objectsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_objectsCheckboxActionPerformed
@ -128,24 +107,29 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox objectsCheckbox;
private javax.swing.JList<String> objectsList;
private javax.swing.JScrollPane objectsScrollPane;
private org.sleuthkit.autopsy.guiutils.CheckBoxListPanel<String> objectsList;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
boolean hasObjects = objectsList.getModel().getSize() > 0;
void configurePanel(boolean selected, List<?> selectedItems) {
boolean hasObjects = isFilterSupported();
objectsCheckbox.setEnabled(hasObjects);
objectsCheckbox.setSelected(selected && hasObjects);
if (objectsCheckbox.isEnabled() && objectsCheckbox.isSelected()) {
objectsScrollPane.setEnabled(true);
objectsList.setEnabled(true);
if (indicesSelected != null) {
objectsList.setSelectedIndices(indicesSelected);
if (selectedItems != null) {
List<String> objectList = new ArrayList<>();
for (Object item : selectedItems) {
if (item instanceof String) {
objectList.add((String) item);
}
}
if (!objectList.isEmpty()) {
objectsList.setSelectedElements(objectList);
}
}
} else {
objectsScrollPane.setEnabled(false);
objectsList.setEnabled(false);
}
}
@ -165,25 +149,30 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
@NbBundle.Messages({"ObjectDetectedFilterPanel.error.text=At least one object type name must be selected."})
@Override
String checkForError() {
if (objectsCheckbox.isSelected() && objectsList.getSelectedValuesList().isEmpty()) {
if (objectsCheckbox.isSelected() && objectsList.getSelectedElements().isEmpty()) {
return Bundle.ObjectDetectedFilterPanel_error_text();
}
return "";
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return objectsList;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (objectsCheckbox.isSelected()) {
return new SearchFiltering.ObjectDetectionFilter(objectsList.getSelectedValuesList());
List<String> objectDetectedList = objectsList.getSelectedElements();
return new SearchFiltering.ObjectDetectionFilter(objectDetectedList);
}
return null;
}
@Override
void addListSelectionListener(ListSelectionListener listener) {
objectsList.addListSelectionListener(listener);
}
@Override
boolean isFilterSupported() {
return !objectsList.isEmpty();
}
}

View File

@ -24,7 +24,7 @@ import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
@ -244,7 +244,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
void configurePanel(boolean selected, List<?> selectedItems) {
parentCheckbox.setSelected(selected);
if (parentCheckbox.isEnabled() && parentCheckbox.isSelected()) {
parentScrollPane.setEnabled(true);
@ -257,9 +257,6 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
deleteButton.setEnabled(!parentListModel.isEmpty());
parentList.setEnabled(true);
parentTextField.setEnabled(true);
if (indicesSelected != null) {
parentList.setSelectedIndices(indicesSelected);
}
} else {
parentScrollPane.setEnabled(false);
parentLabel.setEnabled(false);
@ -311,12 +308,6 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
return results;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return parentList;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
@ -325,4 +316,15 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
}
return null;
}
@Override
void addListSelectionListener(ListSelectionListener listener) {
parentList.addListSelectionListener(listener);
}
@Override
boolean isFilterSupported() {
return true;
}
}

View File

@ -42,47 +42,17 @@
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="crFrequencyScrollPane" pref="300" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="crFrequencyScrollPane" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="crFrequencyScrollPane">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 27]"/>
</Property>
</Properties>
<Component class="org.sleuthkit.autopsy.guiutils.CheckBoxListPanel" name="crFrequencyList">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="crFrequencyList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DefaultListModel&lt;Frequency&gt;()" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
<Property name="visibleRowCount" type="int" value="5"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;Frequency&gt;"/>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;SearchData.Frequency&gt;"/>
</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>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -18,16 +18,16 @@
*/
package org.sleuthkit.autopsy.discovery.ui;
import javax.swing.DefaultListModel;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import org.sleuthkit.autopsy.discovery.search.SearchData;
import org.sleuthkit.autopsy.discovery.search.SearchData.Frequency;
import org.sleuthkit.autopsy.discovery.search.SearchData.Type;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
@ -47,6 +47,7 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
initComponents();
this.type = type;
setUpFrequencyFilter();
add(crFrequencyList);
}
/**
@ -54,13 +55,11 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
pastOccurrencesCheckbox = new javax.swing.JCheckBox();
crFrequencyScrollPane = new javax.swing.JScrollPane();
crFrequencyList = new javax.swing.JList<>();
crFrequencyList = new org.sleuthkit.autopsy.guiutils.CheckBoxListPanel<>();
org.openide.awt.Mnemonics.setLocalizedText(pastOccurrencesCheckbox, org.openide.util.NbBundle.getMessage(PastOccurrencesFilterPanel.class, "PastOccurrencesFilterPanel.pastOccurrencesCheckbox.text")); // NOI18N
pastOccurrencesCheckbox.setMaximumSize(new java.awt.Dimension(150, 25));
@ -74,26 +73,8 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
setMinimumSize(new java.awt.Dimension(250, 30));
setPreferredSize(new java.awt.Dimension(250, 30));
crFrequencyScrollPane.setPreferredSize(new java.awt.Dimension(27, 27));
crFrequencyList.setModel(new DefaultListModel<Frequency>());
crFrequencyList.setEnabled(false);
crFrequencyList.setVisibleRowCount(5);
crFrequencyScrollPane.setViewportView(crFrequencyList);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(crFrequencyScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(crFrequencyScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(0, 0, 0))
);
setLayout(new java.awt.BorderLayout());
add(crFrequencyList, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
private void pastOccurrencesCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pastOccurrencesCheckboxActionPerformed
@ -105,45 +86,48 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpFrequencyFilter() {
int count = 0;
DefaultListModel<SearchData.Frequency> frequencyListModel = (DefaultListModel<SearchData.Frequency>) crFrequencyList.getModel();
frequencyListModel.removeAllElements();
crFrequencyList.clearList();
if (!CentralRepository.isEnabled()) {
if (type != Type.DOMAIN) {
for (SearchData.Frequency freq : SearchData.Frequency.getOptionsForFilteringWithoutCr()) {
frequencyListModel.add(count, freq);
crFrequencyList.addElement(freq.toString(), null, freq);
}
}
} else {
for (SearchData.Frequency freq : SearchData.Frequency.getOptionsForFilteringWithCr()) {
if (type != Type.DOMAIN || freq != SearchData.Frequency.KNOWN) {
frequencyListModel.add(count, freq);
crFrequencyList.addElement(freq.toString(), null, freq);
}
}
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JList<Frequency> crFrequencyList;
private javax.swing.JScrollPane crFrequencyScrollPane;
private org.sleuthkit.autopsy.guiutils.CheckBoxListPanel<SearchData.Frequency> crFrequencyList;
private javax.swing.JCheckBox pastOccurrencesCheckbox;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
void configurePanel(boolean selected, List<?> selectedItems) {
boolean canBeFilteredOn = type != Type.DOMAIN || CentralRepository.isEnabled();
pastOccurrencesCheckbox.setEnabled(canBeFilteredOn);
pastOccurrencesCheckbox.setSelected(selected && canBeFilteredOn);
if (pastOccurrencesCheckbox.isEnabled() && pastOccurrencesCheckbox.isSelected()) {
crFrequencyScrollPane.setEnabled(true);
crFrequencyList.setEnabled(true);
if (indicesSelected != null) {
crFrequencyList.setSelectedIndices(indicesSelected);
if (selectedItems != null) {
List<SearchData.Frequency> frequencyList = new ArrayList<>();
for (Object item : selectedItems) {
if (item instanceof SearchData.Frequency) {
frequencyList.add((SearchData.Frequency) item);
}
}
if (!frequencyList.isEmpty()) {
crFrequencyList.setSelectedElements(frequencyList);
}
}
} else {
crFrequencyScrollPane.setEnabled(false);
crFrequencyList.setEnabled(false);
}
}
@ -163,24 +147,30 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
@NbBundle.Messages({"PastOccurrencesFilterPanel.error.text=At least one value in the past occurrence filter must be selected."})
@Override
String checkForError() {
if (pastOccurrencesCheckbox.isSelected() && crFrequencyList.getSelectedValuesList().isEmpty()) {
if (pastOccurrencesCheckbox.isSelected() && crFrequencyList.getSelectedElements().isEmpty()) {
return Bundle.PastOccurrencesFilterPanel_error_text();
}
return "";
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return crFrequencyList;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (pastOccurrencesCheckbox.isSelected()) {
return new SearchFiltering.FrequencyFilter(crFrequencyList.getSelectedValuesList());
List<SearchData.Frequency> frequencies = crFrequencyList.getSelectedElements();
return new SearchFiltering.FrequencyFilter(frequencies);
}
return null;
}
@Override
void addListSelectionListener(ListSelectionListener listener) {
crFrequencyList.addListSelectionListener(listener);
}
@Override
boolean isFilterSupported() {
return !crFrequencyList.isEmpty();
}
}

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