mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Merge remote-tracking branch 'upstream/develop' into 7393_hostDoc
This commit is contained in:
commit
7f53869448
@ -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. -->
|
||||
|
@ -0,0 +1,4 @@
|
||||
CTL_ResetWindowsAction=Reset Windows
|
||||
ResetWindowAction.caseCloseFailure.text=Unable to close the current case, the software will restart and the windows locations will reset the next time the software is closed.
|
||||
ResetWindowAction.caseSaveMetadata.text=Unable to save current case path, the software will restart and the windows locations will reset but the current case will not be opened upon restart.
|
||||
ResetWindowAction.confirm.text=In order to perform the resetting of window locations the software will close and restart. If a case is currently open, it will be closed. If ingest or a search is currently running, it will be terminated. Are you sure you want to restart the software to reset all window locations?
|
141
Core/src/org/sleuthkit/autopsy/apputils/ResetWindowsAction.java
Normal file
141
Core/src/org/sleuthkit/autopsy/apputils/ResetWindowsAction.java
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Autopsy
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.apputils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.openide.LifecycleManager;
|
||||
import org.openide.awt.ActionID;
|
||||
import org.openide.awt.ActionReference;
|
||||
import org.openide.awt.ActionReferences;
|
||||
import org.openide.awt.ActionRegistration;
|
||||
import org.openide.util.HelpCtx;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.actions.CallableSystemAction;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.CaseActionException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
|
||||
/**
|
||||
* Class to open the Discovery dialog. Allows the user to run searches and see
|
||||
* results in the DiscoveryTopComponent.
|
||||
*/
|
||||
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.apputils.ResetWindowsAction")
|
||||
@ActionReferences(value = {
|
||||
@ActionReference(path = "Menu/Window", position = 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
|
||||
}
|
||||
}
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -218,7 +218,7 @@ public final class CaseMetadata {
|
||||
*
|
||||
* @return The path to the metadata file
|
||||
*/
|
||||
Path getFilePath() {
|
||||
public Path getFilePath() {
|
||||
return metadataFilePath;
|
||||
}
|
||||
|
||||
|
@ -284,10 +284,10 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
ingestStream = IngestManager.getInstance().openIngestStream(image, settings);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error starting ingest modules", ex);
|
||||
final List<String> errors = new ArrayList<>();
|
||||
errors.add(ex.getMessage());
|
||||
callBack.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
|
||||
return;
|
||||
// There was an error with ingest, but the data source has already been added
|
||||
// so proceed with the defaultIngestStream. Code in openIngestStream
|
||||
// should have caused a dialog to popup with the errors.
|
||||
ingestStream = new DefaultIngestStream();
|
||||
}
|
||||
|
||||
doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, progress, callBack);
|
||||
|
@ -55,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);
|
||||
|
@ -58,7 +58,6 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel {
|
||||
lbPointOfContactPhoneText.setVisible(false);
|
||||
lbPointOfContactEmailLabel.setVisible(false);
|
||||
lbPointOfContactEmailText.setVisible(false);
|
||||
setUpCaseDetailsFields();
|
||||
}
|
||||
|
||||
OptionalCasePropertiesPanel(boolean editCurrentCase) {
|
||||
|
@ -37,11 +37,8 @@ class SolrNotConfiguredDialog extends javax.swing.JDialog {
|
||||
SolrNotConfiguredDialog() {
|
||||
super((JFrame) WindowManager.getDefault().getMainWindow(), true);
|
||||
// Center the startup window.
|
||||
Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
int width = getSize().width;
|
||||
int height = getSize().height;
|
||||
setLocation((screenDimension.width - width) / 2, (screenDimension.height - height) / 2);
|
||||
initComponents();
|
||||
setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
|
||||
setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/warning16.png", false));
|
||||
}
|
||||
|
||||
|
@ -18,12 +18,18 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.logging.Level;
|
||||
import org.netbeans.spi.sendopts.OptionProcessor;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.apputils.ResetWindowsAction;
|
||||
import org.sleuthkit.autopsy.commandlineingest.CommandLineIngestManager;
|
||||
import org.sleuthkit.autopsy.commandlineingest.CommandLineOpenCaseManager;
|
||||
import org.sleuthkit.autopsy.commandlineingest.CommandLineOptionProcessor;
|
||||
@ -61,6 +67,13 @@ 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
|
||||
@ -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() {
|
||||
|
@ -33,6 +33,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
*/
|
||||
public class HostsEvent extends TskDataModelChangeEvent<Host> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Retrieves a list of ids from a list of hosts.
|
||||
*
|
||||
@ -42,7 +44,7 @@ public class HostsEvent extends TskDataModelChangeEvent<Host> {
|
||||
private static List<Long> getIds(List<Host> hosts) {
|
||||
return getSafeList(hosts).stream()
|
||||
.filter(h -> h != null)
|
||||
.map(h -> h.getId()).collect(Collectors.toList());
|
||||
.map(h -> h.getHostId()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,7 +78,7 @@ public class HostsEvent extends TskDataModelChangeEvent<Host> {
|
||||
continue;
|
||||
}
|
||||
|
||||
Optional<Host> thisHostOpt = hostManager.getHost(id);
|
||||
Optional<Host> thisHostOpt = hostManager.getHostById(id);
|
||||
thisHostOpt.ifPresent((h) -> toRet.add(h));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule.events;
|
||||
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.events.AutopsyEvent;
|
||||
|
||||
/**
|
||||
* Event published when an OsAccount is deleted.
|
||||
*
|
||||
* oldValue will contain the objectId of the account that was removed. newValue
|
||||
* will be null.
|
||||
*/
|
||||
public final class OsAccountDeletedEvent extends AutopsyEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public OsAccountDeletedEvent(Long osAccountObjectId) {
|
||||
super(Case.Events.OS_ACCOUNT_REMOVED.toString(), osAccountObjectId, null);
|
||||
}
|
||||
}
|
@ -56,7 +56,7 @@ class OsAccountEvent extends TskDataModelChangeEvent<OsAccount> {
|
||||
@Override
|
||||
protected List<OsAccount> getDataModelObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
|
||||
Long id = ids.get(0);
|
||||
OsAccount account = caseDb.getOsAccountManager().getOsAccount(id);
|
||||
OsAccount account = caseDb.getOsAccountManager().getOsAccountByObjectId(id);
|
||||
List<OsAccount> accounts = new ArrayList<>();
|
||||
accounts.add(account);
|
||||
return accounts;
|
||||
|
@ -25,7 +25,7 @@ import org.sleuthkit.datamodel.Person;
|
||||
/**
|
||||
* Event fired when persons are removed.
|
||||
*/
|
||||
public class PersonsRemovedEvent extends PersonsEvent {
|
||||
public class PersonsDeletedEvent extends PersonsEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ -33,7 +33,7 @@ public class PersonsRemovedEvent extends PersonsEvent {
|
||||
* Main constructor.
|
||||
* @param dataModelObjects The list of persons that have been deleted.
|
||||
*/
|
||||
public PersonsRemovedEvent(List<Person> dataModelObjects) {
|
||||
public PersonsDeletedEvent(List<Person> dataModelObjects) {
|
||||
super(Case.Events.PERSONS_DELETED.name(), dataModelObjects);
|
||||
}
|
||||
}
|
@ -42,7 +42,7 @@ public class PersonsEvent extends TskDataModelChangeEvent<Person> {
|
||||
private static List<Long> getIds(List<Person> persons) {
|
||||
return getSafeList(persons).stream()
|
||||
.filter(h -> h != null)
|
||||
.map(h -> h.getId()).collect(Collectors.toList());
|
||||
.map(h -> h.getPersonId()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -26,8 +26,10 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* This class represents an association between a Persona and an Account.
|
||||
@ -206,10 +208,15 @@ public class PersonaAccount {
|
||||
);
|
||||
|
||||
// create account
|
||||
CentralRepoAccount.CentralRepoAccountType crAccountType = getCRInstance().getAccountTypeByName(rs.getString("type_name"));
|
||||
String accountTypeName = rs.getString("type_name");
|
||||
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = getCRInstance().getAccountTypeByName(accountTypeName);
|
||||
if (! optCrAccountType.isPresent()) {
|
||||
// The CR account can not be null, so throw an exception
|
||||
throw new CentralRepoException("Account type with name '" + accountTypeName + "' not found in Central Repository");
|
||||
}
|
||||
CentralRepoAccount account = new CentralRepoAccount(
|
||||
rs.getInt("account_id"),
|
||||
crAccountType,
|
||||
optCrAccountType.get(),
|
||||
rs.getString("account_unique_identifier"));
|
||||
|
||||
// create persona account
|
||||
@ -389,10 +396,15 @@ public class PersonaAccount {
|
||||
while (rs.next()) {
|
||||
|
||||
// create account
|
||||
CentralRepoAccount.CentralRepoAccountType crAccountType = getCRInstance().getAccountTypeByName(rs.getString("type_name"));
|
||||
String accountTypeName = rs.getString("type_name");
|
||||
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = getCRInstance().getAccountTypeByName(accountTypeName);
|
||||
if (! optCrAccountType.isPresent()) {
|
||||
// The CR account can not be null, so throw an exception
|
||||
throw new CentralRepoException("Account type with name '" + accountTypeName + "' not found in Central Repository");
|
||||
}
|
||||
CentralRepoAccount account = new CentralRepoAccount(
|
||||
rs.getInt("account_id"),
|
||||
crAccountType,
|
||||
optCrAccountType.get(),
|
||||
rs.getString("account_unique_identifier"));
|
||||
|
||||
accountsList.add(account);
|
||||
|
@ -37,6 +37,7 @@ import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -78,7 +79,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
private static final int CASE_CACHE_TIMEOUT = 5;
|
||||
private static final int DATA_SOURCE_CACHE_TIMEOUT = 5;
|
||||
private static final int ACCOUNTS_CACHE_TIMEOUT = 5;
|
||||
private static final Cache<String, CentralRepoAccountType> accountTypesCache = CacheBuilder.newBuilder().build();
|
||||
private static final Cache<String, Optional<CentralRepoAccountType>> accountTypesCache = CacheBuilder.newBuilder().build();
|
||||
private static final Cache<Pair<CentralRepoAccountType, String>, CentralRepoAccount> accountsCache = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(ACCOUNTS_CACHE_TIMEOUT, TimeUnit.MINUTES).
|
||||
build();
|
||||
@ -1115,7 +1116,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException {
|
||||
public Optional<CentralRepoAccountType> getAccountTypeByName(String accountTypeName) throws CentralRepoException {
|
||||
try {
|
||||
return accountTypesCache.get(accountTypeName, () -> getCRAccountTypeFromDb(accountTypeName));
|
||||
} catch (CacheLoader.InvalidCacheLoadException | ExecutionException ex) {
|
||||
@ -1155,7 +1156,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
*
|
||||
* @throws CentralRepoException
|
||||
*/
|
||||
private CentralRepoAccountType getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException {
|
||||
private Optional<CentralRepoAccountType> getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException {
|
||||
|
||||
String sql = "SELECT * FROM account_types WHERE type_name = ?";
|
||||
try (Connection conn = connect();
|
||||
@ -1166,10 +1167,11 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
if (resultSet.next()) {
|
||||
Account.Type acctType = new Account.Type(accountTypeName, resultSet.getString("display_name"));
|
||||
CentralRepoAccountType crAccountType = new CentralRepoAccountType(resultSet.getInt("id"), acctType, resultSet.getInt("correlation_type_id"));
|
||||
accountTypesCache.put(accountTypeName, crAccountType);
|
||||
return crAccountType;
|
||||
accountTypesCache.put(accountTypeName, Optional.of(crAccountType));
|
||||
return Optional.of(crAccountType);
|
||||
} else {
|
||||
throw new CentralRepoException("Failed to find entry for account type = " + accountTypeName);
|
||||
accountTypesCache.put(accountTypeName, Optional.empty());
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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.",
|
||||
|
@ -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()) {
|
||||
|
@ -48,7 +48,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* usage, if known.
|
||||
*
|
||||
*/
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 7)
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 8)
|
||||
public final class ContextViewer extends javax.swing.JPanel implements DataContentViewer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
@ -1,3 +1,4 @@
|
||||
OsAccountDataPanel_administrator_title=Administrator
|
||||
OsAccountDataPanel_basic_address=Address
|
||||
OsAccountDataPanel_basic_admin=Administrator
|
||||
OsAccountDataPanel_basic_creationDate=Creation Date
|
||||
@ -5,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
|
||||
|
112
Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountDataPanel.java
Executable file → Normal file
112
Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountDataPanel.java
Executable file → Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,14 +11,20 @@
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="mainScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[200, 0]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
|
@ -18,7 +18,10 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.contentviewers.osaccount;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.Node;
|
||||
@ -34,7 +37,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
/**
|
||||
* DataContentViewer for OsAccounts.
|
||||
*/
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 12)
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 7)
|
||||
public class OsAccountViewer extends javax.swing.JPanel implements DataContentViewer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@ -53,26 +56,29 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
|
||||
|
||||
@Override
|
||||
public void setNode(Node node) {
|
||||
OsAccount osAccount = null;
|
||||
Long osAccountId = null;
|
||||
try {
|
||||
osAccount = node.getLookup().lookup(OsAccount.class);
|
||||
if (osAccount == null) {
|
||||
Optional<OsAccount> optional;
|
||||
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
|
||||
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="heapDumpFileField">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapDumpFileField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="heapDumpBrowseButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapDumpBrowseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="heapDumpBrowseButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="heapFieldValidationLabel">
|
||||
<Properties>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="0" red="ff" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapFieldValidationLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="tempDirectoryPanel">
|
||||
@ -462,7 +521,7 @@
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace pref="158" max="32767" attributes="0"/>
|
||||
<EmptySpace pref="164" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
|
@ -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;
|
||||
|
@ -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=
|
||||
|
@ -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=
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import org.sleuthkit.autopsy.datamodel.OsAccounts.OsAccountNode;
|
||||
|
||||
/**
|
||||
* Visitor Pattern interface that goes over Content nodes in the data source
|
||||
* area of the tree.
|
||||
@ -50,6 +52,9 @@ interface ContentNodeVisitor<T> {
|
||||
|
||||
T visit(BlackboardArtifactNode bban);
|
||||
|
||||
T visit(UnsupportedContentNode ucn);
|
||||
|
||||
T visit(OsAccountNode bban);
|
||||
|
||||
/**
|
||||
* Visitor with an implementable default behavior for all types. Override
|
||||
@ -122,5 +127,15 @@ interface ContentNodeVisitor<T> {
|
||||
public T visit(BlackboardArtifactNode bban) {
|
||||
return defaultVisit(bban);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(UnsupportedContentNode ucn) {
|
||||
return defaultVisit(ucn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(OsAccountNode bban) {
|
||||
return defaultVisit(bban);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import org.sleuthkit.datamodel.Pool;
|
||||
import org.sleuthkit.datamodel.SlackFile;
|
||||
import org.sleuthkit.datamodel.SleuthkitItemVisitor;
|
||||
import org.sleuthkit.datamodel.SleuthkitVisitableItem;
|
||||
import org.sleuthkit.datamodel.UnsupportedContent;
|
||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||
import org.sleuthkit.datamodel.Volume;
|
||||
|
||||
@ -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(),
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -51,7 +51,7 @@ public class HostDataSources implements AutopsyVisitableItem, Comparable<HostDat
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(this.host == null ? 0 : this.host.getId());
|
||||
return Objects.hashCode(this.host == null ? 0 : this.host.getHostId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -66,8 +66,8 @@ public class HostDataSources implements AutopsyVisitableItem, Comparable<HostDat
|
||||
return false;
|
||||
}
|
||||
final HostDataSources other = (HostDataSources) obj;
|
||||
long thisId = (this.getHost() == null) ? 0 : this.getHost().getId();
|
||||
long otherId = (other.getHost() == null) ? 0 : other.getHost().getId();
|
||||
long thisId = (this.getHost() == null) ? 0 : this.getHost().getHostId();
|
||||
long otherId = (other.getHost() == null) ? 0 : other.getHost().getHostId();
|
||||
return thisId == otherId;
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class HostGrouping implements AutopsyVisitableItem, Comparable<HostGroupi
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(this.host == null ? 0 : this.host.getId());
|
||||
return Objects.hashCode(this.host == null ? 0 : this.host.getHostId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -66,8 +66,8 @@ public class HostGrouping implements AutopsyVisitableItem, Comparable<HostGroupi
|
||||
return false;
|
||||
}
|
||||
final HostGrouping other = (HostGrouping) obj;
|
||||
long thisId = (this.getHost() == null) ? 0 : this.getHost().getId();
|
||||
long otherId = (other.getHost() == null) ? 0 : other.getHost().getId();
|
||||
long thisId = (this.getHost() == null) ? 0 : this.getHost().getHostId();
|
||||
long otherId = (other.getHost() == null) ? 0 : other.getHost().getHostId();
|
||||
return thisId == otherId;
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
@ -143,10 +154,10 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
||||
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 (evt.getPropertyName().equals(Case.Events.OS_ACCOUNT_CHANGED.name())) {
|
||||
if (((OsAccountChangedEvent) evt).getOsAccount().getId() == account.getId()) {
|
||||
// Update the account node to the new one
|
||||
account = ((OsAccountChangedEvent) evt).getOsAccount();
|
||||
updateSheet();
|
||||
}
|
||||
} else if (evt.getPropertyName().equals(REALM_DATA_AVAILABLE_EVENT)) {
|
||||
OsAccountRealm realm = (OsAccountRealm) evt.getNewValue();
|
||||
|
||||
// Currently only 0 or 1 names are supported, this will need
|
||||
// to be modified if that changes.
|
||||
List<String> realmNames = realm.getRealmNames();
|
||||
if (!realmNames.isEmpty()) {
|
||||
updateSheet(new NodeProperty<>(
|
||||
Bundle.OsAccounts_accountRealmNameProperty_name(),
|
||||
Bundle.OsAccounts_accountRealmNameProperty_displayName(),
|
||||
Bundle.OsAccounts_accountRealmNameProperty_desc(),
|
||||
""));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
|
||||
|
||||
/**
|
||||
* Constructs a new OsAccountNode.
|
||||
*
|
||||
* @param account Node object.
|
||||
*/
|
||||
OsAccountNode(OsAccount account) {
|
||||
super(Children.LEAF, Lookups.fixed(account));
|
||||
super(account);
|
||||
this.account = account;
|
||||
|
||||
setName(account.getName());
|
||||
setDisplayName(account.getName());
|
||||
setIconBaseWithExtension(ICON_PATH);
|
||||
|
||||
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), listener);
|
||||
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), weakListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ public class PersonGrouping implements AutopsyVisitableItem, Comparable<PersonGr
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(this.person == null ? 0 : this.person.getId());
|
||||
return Objects.hashCode(this.person == null ? 0 : this.person.getPersonId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -67,8 +67,8 @@ public class PersonGrouping implements AutopsyVisitableItem, Comparable<PersonGr
|
||||
return false;
|
||||
}
|
||||
final PersonGrouping other = (PersonGrouping) obj;
|
||||
long thisId = (this.getPerson() == null) ? 0 : this.getPerson().getId();
|
||||
long otherId = (other.getPerson() == null) ? 0 : other.getPerson().getId();
|
||||
long thisId = (this.getPerson() == null) ? 0 : this.getPerson().getPersonId();
|
||||
long otherId = (other.getPerson() == null) ? 0 : other.getPerson().getPersonId();
|
||||
return thisId == otherId;
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ public class PersonGroupingNode extends DisplayableItemNode {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (personId != null && eventType.equals(Case.Events.PERSONS_CHANGED.toString()) && evt instanceof PersonsChangedEvent) {
|
||||
((PersonsChangedEvent) evt).getNewValue().stream()
|
||||
.filter(p -> p != null && p.getId() == personId)
|
||||
.filter(p -> p != null && p.getPersonId() == personId)
|
||||
.findFirst()
|
||||
.ifPresent((newPerson) -> {
|
||||
setName(newPerson.getName());
|
||||
@ -191,7 +191,7 @@ public class PersonGroupingNode extends DisplayableItemNode {
|
||||
super.setDisplayName(displayName);
|
||||
this.setIconBaseWithExtension(ICON_PATH);
|
||||
this.person = person;
|
||||
this.personId = person == null ? null : person.getId();
|
||||
this.personId = person == null ? null : person.getPersonId();
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.PERSONS_CHANGED),
|
||||
WeakListeners.propertyChange(personChangePcl, this));
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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()) {
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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());
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
@ -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=...
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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<ArtifactTypeItem>()" 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="<ArtifactTypeItem>"/>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<BlackboardArtifact.ARTIFACT_TYPE>"/>
|
||||
</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>
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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<DataSourceItem>()" 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="<DataSourceItem>"/>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<DataSource>"/>
|
||||
</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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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<String>()" 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="<String>"/>
|
||||
</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>
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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<String>()" 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="<String>"/>
|
||||
</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>
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<String>()" 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="<String>"/>
|
||||
</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>
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Frequency>()" 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="<Frequency>"/>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<SearchData.Frequency>"/>
|
||||
</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>
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user