Merge branch 'develop' of github.com:sleuthkit/autopsy into 7505-tempPathChanges

This commit is contained in:
Greg DiCristofaro
2021-04-14 11:27:47 -04:00
32 changed files with 733 additions and 226 deletions
@@ -1,3 +1,4 @@
CTL_ResetWindowsAction=Reset Windows
ResetWindowAction.confirm.text=The program will close and restart to perform the resetting of window locations.\n\nAre you sure you want to reset all window locations?
ResetWindowAction.confirm.title=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?
@@ -20,8 +20,8 @@ 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.JOptionPane;
import javax.swing.SwingUtilities;
import org.apache.commons.io.FileUtils;
import org.openide.LifecycleManager;
@@ -32,9 +32,10 @@ import org.openide.awt.ActionRegistration;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction;
import org.openide.windows.WindowManager;
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;
/**
@@ -51,39 +52,66 @@ 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 !Case.isCaseOpen();
return true;
}
@NbBundle.Messages({"ResetWindowAction.confirm.title=Reset Windows",
"ResetWindowAction.confirm.text=The program will close and restart to perform the resetting of window locations.\n\nAre you sure you want to reset all window locations?"})
@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(() -> {
int response = JOptionPane.showConfirmDialog(
WindowManager.getDefault().getMainWindow(),
Bundle.ResetWindowAction_confirm_text(),
Bundle.ResetWindowAction_confirm_title(),
JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE);
if (response == JOptionPane.YES_OPTION) {
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"));
FileUtils.deleteDirectory(new File(PlatformUtil.getUserConfigDirectory() + File.separator + WINDOWS2LOCAL));
} catch (IOException ex) {
logger.log(Level.WARNING, "Unable to delete config directory, window locations will not be reset.", 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);
}
}
});
LifecycleManager.getDefault().markForRestart();
LifecycleManager.getDefault().exit();
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
@@ -91,6 +119,7 @@ public final class ResetWindowsAction extends CallableSystemAction {
* @param value whether to enable this action or not
*/
@Override
public void setEnabled(boolean value) {
super.setEnabled(value);
}
@@ -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
@@ -1934,7 +1934,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);
@@ -18,12 +18,18 @@
*/
package org.sleuthkit.autopsy.casemodule;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Iterator;
import java.util.logging.Level;
import org.netbeans.spi.sendopts.OptionProcessor;
import javax.swing.SwingUtilities;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.apputils.ResetWindowsAction;
import org.sleuthkit.autopsy.commandlineingest.CommandLineIngestManager;
import org.sleuthkit.autopsy.commandlineingest.CommandLineOpenCaseManager;
import org.sleuthkit.autopsy.commandlineingest.CommandLineOptionProcessor;
@@ -61,15 +67,22 @@ public class StartupWindowProvider implements StartupWindowInterface {
return instance;
}
@NbBundle.Messages({
"# {0} - autFilePath",
"StartupWindowProvider.openCase.noFile=Unable to open previously open case because metadata file not found at: {0}",
"# {0} - reOpenFilePath",
"StartupWindowProvider.openCase.deleteOpenFailure=Unable to open or delete file containing path {0} to previously open case. The previous case will not be opened.",
"# {0} - autFilePath",
"StartupWindowProvider.openCase.cantOpen=Unable to open previously open case with metadata file: {0}"})
private void init() {
if (startupWindowToUse == null) {
// first check whether we are running from command line
if (isRunningFromCommandLine()) {
String defaultArg = getDefaultArgument();
if(defaultArg != null) {
new CommandLineOpenCaseManager(defaultArg).start();
return;
if (defaultArg != null) {
new CommandLineOpenCaseManager(defaultArg).start();
return;
} else {
// Autopsy is running from command line
logger.log(Level.INFO, "Running from command line"); //NON-NLS
@@ -83,36 +96,41 @@ public class StartupWindowProvider implements StartupWindowInterface {
if (RuntimeProperties.runningWithGUI()) {
checkSolr();
}
//discover the registered windows
Collection<? extends StartupWindowInterface> startupWindows
= Lookup.getDefault().lookupAll(StartupWindowInterface.class);
int windowsCount = startupWindows.size();
if (windowsCount == 1) {
startupWindowToUse = startupWindows.iterator().next();
logger.log(Level.INFO, "Will use the default startup window: " + startupWindowToUse.toString()); //NON-NLS
} else if (windowsCount == 2) {
//pick the non default one
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
while (it.hasNext()) {
StartupWindowInterface window = it.next();
if (!org.sleuthkit.autopsy.casemodule.StartupWindow.class.isInstance(window)) {
startupWindowToUse = window;
logger.log(Level.INFO, "Will use the custom startup window: " + startupWindowToUse.toString()); //NON-NLS
break;
switch (windowsCount) {
case 1:
startupWindowToUse = startupWindows.iterator().next();
logger.log(Level.INFO, "Will use the default startup window: {0}", startupWindowToUse.toString()); //NON-NLS
break;
case 2: {
//pick the non default one
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
while (it.hasNext()) {
StartupWindowInterface window = it.next();
if (!org.sleuthkit.autopsy.casemodule.StartupWindow.class.isInstance(window)) {
startupWindowToUse = window;
logger.log(Level.INFO, "Will use the custom startup window: {0}", startupWindowToUse.toString()); //NON-NLS
break;
}
}
break;
}
} else {
// select first non-Autopsy start up window
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
while (it.hasNext()) {
StartupWindowInterface window = it.next();
if (!window.getClass().getCanonicalName().startsWith("org.sleuthkit.autopsy")) {
startupWindowToUse = window;
logger.log(Level.INFO, "Will use the custom startup window: " + startupWindowToUse.toString()); //NON-NLS
break;
default: {
// select first non-Autopsy start up window
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
while (it.hasNext()) {
StartupWindowInterface window = it.next();
if (!window.getClass().getCanonicalName().startsWith("org.sleuthkit.autopsy")) {
startupWindowToUse = window;
logger.log(Level.INFO, "Will use the custom startup window: {0}", startupWindowToUse.toString()); //NON-NLS
break;
}
}
break;
}
}
@@ -121,6 +139,45 @@ public class StartupWindowProvider implements StartupWindowInterface {
startupWindowToUse = new org.sleuthkit.autopsy.casemodule.StartupWindow();
}
}
File openPreviousCaseFile = new File(ResetWindowsAction.getCaseToReopenFilePath());
if (openPreviousCaseFile.exists()) {
//do actual opening on another thread
new Thread(() -> {
String caseFilePath = "";
String unableToOpenMessage = null;
try {
//avoid readFileToString having ambiguous arguments
Charset encoding = null;
caseFilePath = FileUtils.readFileToString(openPreviousCaseFile, encoding);
if (new File(caseFilePath).exists()) {
FileUtils.forceDelete(openPreviousCaseFile);
//close the startup window as we attempt to open the case
close();
Case.openAsCurrentCase(caseFilePath);
} else {
unableToOpenMessage = Bundle.StartupWindowProvider_openCase_noFile(caseFilePath);
logger.log(Level.WARNING, unableToOpenMessage);
}
} catch (IOException ex) {
unableToOpenMessage = Bundle.StartupWindowProvider_openCase_deleteOpenFailure(ResetWindowsAction.getCaseToReopenFilePath());
logger.log(Level.WARNING, unableToOpenMessage, ex);
} catch (CaseActionException ex) {
unableToOpenMessage = Bundle.StartupWindowProvider_openCase_cantOpen(caseFilePath);
logger.log(Level.WARNING, unableToOpenMessage, ex);
}
if (RuntimeProperties.runningWithGUI() && !StringUtils.isBlank(unableToOpenMessage)) {
final String message = unableToOpenMessage;
SwingUtilities.invokeLater(() -> {
MessageNotifyUtil.Message.warn(message);
//the case was not opened restore the startup window
open();
});
}
}).start();
}
}
private void checkSolr() {
@@ -147,9 +204,9 @@ public class StartupWindowProvider implements StartupWindowInterface {
* @return True if running from command line, false otherwise
*/
private boolean isRunningFromCommandLine() {
CommandLineOptionProcessor processor = Lookup.getDefault().lookup(CommandLineOptionProcessor.class);
if(processor != null) {
if (processor != null) {
return processor.isRunFromCommandLine();
}
return false;
@@ -157,12 +214,12 @@ public class StartupWindowProvider implements StartupWindowInterface {
/**
* Get the default argument from the CommandLineOptionProcessor.
*
* @return If set, the default argument otherwise null.
*
* @return If set, the default argument otherwise null.
*/
private String getDefaultArgument() {
private String getDefaultArgument() {
CommandLineOptionProcessor processor = Lookup.getDefault().lookup(CommandLineOptionProcessor.class);
if(processor != null) {
if (processor != null) {
return processor.getDefaultArgument();
}
return null;
@@ -301,7 +301,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
/**
* Values for configuration located in the /etc/*.conf file.
* Values for configuration located in the /etc/\*.conf file.
*/
private static class ConfValues {
private final String XmxVal;
@@ -335,7 +335,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
}
/**
* Retrieve the /etc/*.conf file values pertinent to settings.
* Retrieve the /etc/\*.conf file values pertinent to settings.
* @return The conf file values.
* @throws IOException
*/
@@ -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;
@@ -99,6 +100,11 @@ public class CreateSleuthkitNodeVisitor extends SleuthkitItemVisitor.Default<Abs
public AbstractContentNode<? extends Content> visit(BlackboardArtifact art) {
return new BlackboardArtifactNode(art);
}
@Override
public AbstractContentNode<? extends Content> visit(UnsupportedContent uc) {
return new UnsupportedContentNode(uc);
}
@Override
protected AbstractContentNode<? extends Content> defaultVisit(SleuthkitVisitableItem di) {
@@ -202,6 +202,11 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(HostNode node);
T visit(DataSourcesByTypeNode node);
/*
* Unsupported node
*/
T visit(UnsupportedContentNode ucn);
/**
* Visitor with an implementable default behavior for all types. Override
@@ -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);
}
}
}
@@ -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,9 +124,9 @@ 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);
refresh(true);
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
// case was closed. Remove listeners so that we don't get called with a stale case handle
if (evt.getNewValue() == null) {
@@ -126,22 +136,22 @@ public final class OsAccounts implements AutopsyVisitableItem {
}
}
};
@Override
protected void addNotify() {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.OS_ACCOUNT_ADDED, Case.Events.OS_ACCOUNT_REMOVED), listener);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener);
}
@Override
protected void removeNotify() {
Case.removeEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_ADDED), listener);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener);
}
@Override
protected boolean createKeys(List<OsAccount> list) {
if(skCase != null) {
if (skCase != null) {
try {
if (filteringDSObjId == 0) {
list.addAll(skCase.getOsAccountManager().getOsAccounts());
@@ -166,35 +176,52 @@ public final class OsAccounts implements AutopsyVisitableItem {
/**
* An OsAccount leaf Node.
*/
public static final class OsAccountNode extends DisplayableItemNode {
public static final class OsAccountNode extends AbstractContentNode<OsAccount> {
private OsAccount account;
private final PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if(((OsAccountChangedEvent)evt).getOsAccount().getId() == account.getId()) {
// Update the account node to the new one
account = ((OsAccountChangedEvent)evt).getOsAccount();
updateSheet();
if (evt.getPropertyName().equals(Case.Events.OS_ACCOUNT_CHANGED.name())) {
if (((OsAccountChangedEvent) evt).getOsAccount().getId() == account.getId()) {
// Update the account node to the new one
account = ((OsAccountChangedEvent) evt).getOsAccount();
updateSheet();
}
} else if (evt.getPropertyName().equals(REALM_DATA_AVAILABLE_EVENT)) {
OsAccountRealm realm = (OsAccountRealm) evt.getNewValue();
// Currently only 0 or 1 names are supported, this will need
// to be modified if that changes.
List<String> realmNames = realm.getRealmNames();
if (!realmNames.isEmpty()) {
updateSheet(new NodeProperty<>(
Bundle.OsAccounts_accountRealmNameProperty_name(),
Bundle.OsAccounts_accountRealmNameProperty_displayName(),
Bundle.OsAccounts_accountRealmNameProperty_desc(),
""));
}
}
}
};
private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
/**
* Constructs a new OsAccountNode.
*
* @param account Node object.
*/
OsAccountNode(OsAccount account) {
super(Children.LEAF, Lookups.fixed(account));
super(account);
this.account = account;
setName(account.getName());
setDisplayName(account.getName());
setIconBaseWithExtension(ICON_PATH);
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), listener);
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), weakListener);
}
@Override
@@ -211,7 +238,16 @@ public final class OsAccounts implements AutopsyVisitableItem {
public String getItemType() {
return getClass().getName();
}
/**
* Returns the OsAccount associated with this node.
*
* @return
*/
OsAccount getOsAccount() {
return account;
}
@Messages({
"OsAccounts_accountNameProperty_name=Name",
"OsAccounts_accountNameProperty_displayName=Name",
@@ -226,13 +262,13 @@ public final class OsAccounts implements AutopsyVisitableItem {
"OsAccounts_loginNameProperty_displayName=Login Name",
"OsAccounts_loginNameProperty_desc=Os Account login name"
})
/**
* Refreshes this node's property sheet.
*/
void updateSheet() {
this.setSheet(createSheet());
}
* Refreshes this node's property sheet.
*/
void updateSheet() {
this.setSheet(createSheet());
}
@Override
protected Sheet createSheet() {
@@ -255,9 +291,8 @@ public final class OsAccounts implements AutopsyVisitableItem {
Bundle.OsAccounts_loginNameProperty_displayName(),
Bundle.OsAccounts_loginNameProperty_desc(),
optional.isPresent() ? optional.get() : ""));
// TODO - load realm on background thread
// Fill with empty string, fetch on background task.
String realmName = "";
//String realmName = account.getRealm().getRealmNames().isEmpty() ? "" : account.getRealm().getRealmNames().get(0);
propertiesSet.put(new NodeProperty<>(
Bundle.OsAccounts_accountRealmNameProperty_name(),
Bundle.OsAccounts_accountRealmNameProperty_displayName(),
@@ -274,16 +309,91 @@ public final class OsAccounts implements AutopsyVisitableItem {
Bundle.OsAccounts_createdTimeProperty_desc(),
timeDisplayStr));
backgroundTasksPool.submit(new GetOsAccountRealmTask(new WeakReference<>(this), weakListener));
return sheet;
}
@Override
public Action[] getActions(boolean popup) {
List<Action> actionsList = new ArrayList<>();
actionsList.addAll(Arrays.asList(super.getActions(popup)));
actionsList.addAll(DataModelActionsFactory.getActions(account));
return actionsList.toArray(new Action[actionsList.size()]);
}
@Override
protected List<Tag> getAllTagsFromDatabase() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
return null;
}
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
return null;
}
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
}
@Override
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) {
return null;
}
@Override
public <T> T accept(ContentNodeVisitor<T> visitor) {
return visitor.visit(this);
}
/**
* Task for grabbing the osAccount realm.
*/
static class GetOsAccountRealmTask implements Runnable {
private final WeakReference<OsAccountNode> weakNodeRef;
private final PropertyChangeListener listener;
/**
* Construct a new task.
*
* @param weakContentRef
* @param listener
*/
GetOsAccountRealmTask(WeakReference<OsAccountNode> weakContentRef, PropertyChangeListener listener) {
this.weakNodeRef = weakContentRef;
this.listener = listener;
}
@Override
public void run() {
OsAccountNode node = weakNodeRef.get();
if (node == null) {
return;
}
try {
long realmId = node.getOsAccount().getRealmId();
OsAccountRealm realm = Case.getCurrentCase().getSleuthkitCase().getOsAccountRealmManager().getRealmByRealmId(realmId);
if (listener != null && realm != null) {
listener.propertyChange(new PropertyChangeEvent(
AutopsyEvent.SourceType.LOCAL.toString(),
REALM_DATA_AVAILABLE_EVENT,
null, realm));
}
} catch (TskCoreException ex) {
Exceptions.printStackTrace(ex);
}
}
}
}
}
@@ -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);
}
}
@@ -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);
@@ -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(() -> {
@@ -181,6 +183,13 @@ public class ViewContextAction extends AbstractAction {
logger.log(Level.SEVERE, String.format("Could not get parent of Content object: %s", content), ex); //NON-NLS
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.
@@ -60,7 +60,7 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
objectsList.clearList();
List<String> setNames = DiscoveryUiUtils.getSetNames(BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION);
for (String name : setNames) {
objectsList.addElement(name, null, null);
objectsList.addElement(name, null, name);
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error loading object detected set names", ex);
@@ -31,6 +31,7 @@ import org.sleuthkit.datamodel.LocalFile;
import org.sleuthkit.datamodel.LocalDirectory;
import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.SlackFile;
import org.sleuthkit.datamodel.UnsupportedContent;
import org.sleuthkit.datamodel.VirtualDirectory;
/**
@@ -113,4 +114,9 @@ final class GetRootDirectoryVisitor extends GetFilesContentVisitor {
public Collection<AbstractFile> visit(OsAccount art) {
return getAllFromChildren(art);
}
@Override
public Collection<AbstractFile> visit(UnsupportedContent uc) {
return getAllFromChildren(uc);
}
}