mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-15 01:07:42 +00:00
integrate and update data src deletion
This commit is contained in:
parent
37fb384192
commit
8db2acfe16
@ -16,7 +16,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.casemodule;
|
package org.sleuthkit.autopsy.access;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@ -26,7 +26,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
|||||||
* Class for methods to check if access should be limited to a feature
|
* Class for methods to check if access should be limited to a feature
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
final class AccessLimiterUtils {
|
final public class AccessLimiterUtils {
|
||||||
|
|
||||||
private final static String MULTI_USER_ACCESS_FILE_NAME = "mualimit"; // NON-NLS
|
private final static String MULTI_USER_ACCESS_FILE_NAME = "mualimit"; // NON-NLS
|
||||||
private final static String MULTI_USER_ACCESS_FILE_PATH = Paths.get(PlatformUtil.getUserConfigDirectory(), MULTI_USER_ACCESS_FILE_NAME).toString();
|
private final static String MULTI_USER_ACCESS_FILE_PATH = Paths.get(PlatformUtil.getUserConfigDirectory(), MULTI_USER_ACCESS_FILE_NAME).toString();
|
||||||
@ -36,7 +36,7 @@ final class AccessLimiterUtils {
|
|||||||
*
|
*
|
||||||
* @return True if privileges should be restricted, false otherwise.
|
* @return True if privileges should be restricted, false otherwise.
|
||||||
*/
|
*/
|
||||||
static boolean limitMultiUserAccess() {
|
public static boolean limitMultiUserAccess() {
|
||||||
return new File(MULTI_USER_ACCESS_FILE_PATH).exists();
|
return new File(MULTI_USER_ACCESS_FILE_PATH).exists();
|
||||||
}
|
}
|
||||||
|
|
@ -23,7 +23,6 @@ DeleteContentTagAction.deleteTag=Remove Selected Tag(s)
|
|||||||
DeleteContentTagAction.tagDelErr=Tag Deletion Error
|
DeleteContentTagAction.tagDelErr=Tag Deletion Error
|
||||||
# {0} - tagName
|
# {0} - tagName
|
||||||
DeleteContentTagAction.unableToDelTag.msg=Unable to delete tag {0}.
|
DeleteContentTagAction.unableToDelTag.msg=Unable to delete tag {0}.
|
||||||
DeleteDataSourceAction.name.text=Delete Data Source
|
|
||||||
DeleteFileBlackboardArtifactTagAction.deleteTag=Remove Result Tag
|
DeleteFileBlackboardArtifactTagAction.deleteTag=Remove Result Tag
|
||||||
# {0} - artifactID
|
# {0} - artifactID
|
||||||
DeleteFileBlackboardArtifactTagAction.deleteTag.alert=Unable to untag artifact {0}.
|
DeleteFileBlackboardArtifactTagAction.deleteTag.alert=Unable to untag artifact {0}.
|
||||||
|
@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2019 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.actions;
|
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import javax.swing.AbstractAction;
|
|
||||||
import org.openide.util.Lookup;
|
|
||||||
import org.openide.util.NbBundle;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
|
|
||||||
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException;
|
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An Action that allows a user to delete a data source.
|
|
||||||
*/
|
|
||||||
public final class DeleteDataSourceAction extends AbstractAction {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName());
|
|
||||||
private long dataSourceID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an Action that allows a user to delete a data source.
|
|
||||||
*
|
|
||||||
* @param dataSourceID The object ID of the data source to be deleted.
|
|
||||||
*/
|
|
||||||
@NbBundle.Messages({
|
|
||||||
"DeleteDataSourceAction.name.text=Delete Data Source"
|
|
||||||
})
|
|
||||||
public DeleteDataSourceAction(Long dataSourceID) {
|
|
||||||
super(Bundle.DeleteDataSourceAction_name_text());
|
|
||||||
this.dataSourceID = dataSourceID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent event) {
|
|
||||||
// try {
|
|
||||||
// Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(dataSourceID);
|
|
||||||
// KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class);
|
|
||||||
// kwsService.deleteDataSource(dataSourceID);
|
|
||||||
// Case.getCurrentCaseThrows().notifyDataSourceDeleted(dataSourceID);
|
|
||||||
// } catch (NoCurrentCaseException | TskCoreException | KeywordSearchServiceException e) {
|
|
||||||
// logger.log(Level.SEVERE, String.format("Error Deleting data source (obj_id=%d)", dataSourceID), e);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeleteDataSourceAction clone() throws CloneNotSupportedException {
|
|
||||||
DeleteDataSourceAction clonedObject = ((DeleteDataSourceAction) super.clone());
|
|
||||||
clonedObject.setDataSourceID(this.dataSourceID);
|
|
||||||
return clonedObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setDataSourceID(long dataSourceID) {
|
|
||||||
this.dataSourceID = dataSourceID;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -10,7 +10,6 @@ Case.deleteCaseConfirmationDialog.title=Delete Current Case?
|
|||||||
Case.deleteCaseFailureMessageBox.message=Error deleting case: {0}
|
Case.deleteCaseFailureMessageBox.message=Error deleting case: {0}
|
||||||
Case.deleteCaseFailureMessageBox.title=Failed to Delete Case
|
Case.deleteCaseFailureMessageBox.title=Failed to Delete Case
|
||||||
Case.DeletingDataSourceFromCase=Deleting the Data Source from the case.
|
Case.DeletingDataSourceFromCase=Deleting the Data Source from the case.
|
||||||
Case.ErrorDeletingDataSource.name.text=Error Deleting Data Source
|
|
||||||
Case.exceptionMessage.cancelledByUser=Cancelled by user.
|
Case.exceptionMessage.cancelledByUser=Cancelled by user.
|
||||||
Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.
|
Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.
|
||||||
Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host.
|
Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host.
|
||||||
@ -38,6 +37,8 @@ Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination s
|
|||||||
Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled.
|
Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled.
|
||||||
Case.exceptionMessage.emptyCaseDir=Must specify a case directory path.
|
Case.exceptionMessage.emptyCaseDir=Must specify a case directory path.
|
||||||
Case.exceptionMessage.emptyCaseName=Must specify a case name.
|
Case.exceptionMessage.emptyCaseName=Must specify a case name.
|
||||||
|
# {0} - exception message
|
||||||
|
Case.exceptionMessage.errorDeletingDataSource=An error occurred while deleting the data source:\n{0}.
|
||||||
Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details.
|
Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details.
|
||||||
# {0} - exception message
|
# {0} - exception message
|
||||||
Case.exceptionMessage.execExceptionWrapperMessage={0}
|
Case.exceptionMessage.execExceptionWrapperMessage={0}
|
||||||
@ -54,14 +55,11 @@ Case.exceptionMessage.metadataUpdateError=Failed to update case metadata
|
|||||||
Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.
|
Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.
|
||||||
Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User.
|
Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User.
|
||||||
Case.progressIndicatorCancelButton.label=Cancel
|
Case.progressIndicatorCancelButton.label=Cancel
|
||||||
Case.progressIndicatorStatus_closingCase=Closing Case to Deleting Data Source.
|
|
||||||
Case.progressIndicatorStatus_deletingDataSource=Deleting Data Source.
|
|
||||||
Case.progressIndicatorStatus_openingCase=Opening Case to Deleting Data Source.
|
|
||||||
Case.progressIndicatorTitle.closingCase=Closing Case
|
Case.progressIndicatorTitle.closingCase=Closing Case
|
||||||
Case.progressIndicatorTitle.creatingCase=Creating Case
|
Case.progressIndicatorTitle.creatingCase=Creating Case
|
||||||
Case.progressIndicatorTitle.deletingCase=Deleting Case
|
Case.progressIndicatorTitle.deletingCase=Deleting Case
|
||||||
|
Case.progressIndicatorTitle.deletingDataSource=Deleting Data Source
|
||||||
Case.progressIndicatorTitle.openingCase=Opening Case
|
Case.progressIndicatorTitle.openingCase=Opening Case
|
||||||
Case.progressIndicatorTitle_deletingDataSource=Deleting Data Source from the case.
|
|
||||||
Case.progressMessage.cancelling=Cancelling...
|
Case.progressMessage.cancelling=Cancelling...
|
||||||
Case.progressMessage.clearingTempDirectory=Clearing case temp directory...
|
Case.progressMessage.clearingTempDirectory=Clearing case temp directory...
|
||||||
Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources...
|
Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources...
|
||||||
@ -124,6 +122,10 @@ CTL_CaseDetailsAction=Case Details
|
|||||||
CTL_CaseDeleteAction=Delete Case
|
CTL_CaseDeleteAction=Delete Case
|
||||||
CTL_CaseOpenAction=Open Case
|
CTL_CaseOpenAction=Open Case
|
||||||
CTL_UnpackagePortableCaseAction=Unpack and Open Portable Case
|
CTL_UnpackagePortableCaseAction=Unpack and Open Portable Case
|
||||||
|
DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?
|
||||||
|
DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to reopen the case.
|
||||||
|
DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source.\nPlease see the application log for details.
|
||||||
|
DeleteDataSourceAction.name.text=Delete Data Source
|
||||||
EditOptionalCasePropertiesPanel.cancelButton.text=Cancel
|
EditOptionalCasePropertiesPanel.cancelButton.text=Cancel
|
||||||
EditOptionalCasePropertiesPanel.saveButton.text=Save
|
EditOptionalCasePropertiesPanel.saveButton.text=Save
|
||||||
GeneralFilter.encaseImageDesc.text=Encase Images (*.e01)
|
GeneralFilter.encaseImageDesc.text=Encase Images (*.e01)
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.casemodule;
|
package org.sleuthkit.autopsy.casemodule;
|
||||||
|
|
||||||
|
import org.sleuthkit.autopsy.access.AccessLimiterUtils;
|
||||||
import com.google.common.annotations.Beta;
|
import com.google.common.annotations.Beta;
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
|
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
|
||||||
@ -52,6 +53,7 @@ import java.util.concurrent.Executors;
|
|||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.ThreadFactory;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@ -741,9 +743,9 @@ public class Case {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the selected data source.
|
* Deletes a data source from the current cases.
|
||||||
*
|
*
|
||||||
* @param dataSourceId id of the data source to delete.
|
* @param dataSourceObjectID The object ID of the data source to delete.
|
||||||
*
|
*
|
||||||
* @throws CaseActionException If there is a problem deleting the case. The
|
* @throws CaseActionException If there is a problem deleting the case. The
|
||||||
* exception will have a user-friendly message
|
* exception will have a user-friendly message
|
||||||
@ -751,64 +753,20 @@ public class Case {
|
|||||||
* exception.
|
* exception.
|
||||||
*/
|
*/
|
||||||
@Messages({
|
@Messages({
|
||||||
"Case.progressIndicatorTitle_deletingDataSource=Deleting Data Source from the case.",
|
"Case.progressIndicatorTitle.deletingDataSource=Deleting Data Source"
|
||||||
"Case.progressIndicatorStatus_closingCase=Closing Case to Deleting Data Source.",
|
})
|
||||||
"Case.progressIndicatorStatus_openingCase=Opening Case to Deleting Data Source.",
|
public static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException {
|
||||||
"Case.progressIndicatorStatus_deletingDataSource=Deleting Data Source.",})
|
|
||||||
public static void deleteDataSourceFromCurrentCase(Long dataSourceId) throws CaseActionException {
|
|
||||||
synchronized (caseActionSerializationLock) {
|
synchronized (caseActionSerializationLock) {
|
||||||
if (null == currentCase) {
|
if (null == currentCase) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CaseMetadata newMetadata = null;
|
Case theCase = currentCase;
|
||||||
CaseMetadata metadata = currentCase.getMetadata();
|
|
||||||
String caseDir = metadata.getFilePath().toString();
|
|
||||||
try {
|
|
||||||
newMetadata = new CaseMetadata(Paths.get(caseDir));
|
|
||||||
} catch (CaseMetadataException ex) {
|
|
||||||
logger.log(Level.WARNING, String.format("Error Getting Case Dir %s", caseDir), ex);
|
|
||||||
}
|
|
||||||
ProgressIndicator progressIndicator;
|
|
||||||
if (RuntimeProperties.runningWithGUI()) {
|
|
||||||
progressIndicator = new ModalDialogProgressIndicator(mainFrame, Bundle.Case_progressIndicatorTitle_deletingDataSource());
|
|
||||||
} else {
|
|
||||||
progressIndicator = new LoggingProgressIndicator();
|
|
||||||
}
|
|
||||||
closeCurrentCase();
|
closeCurrentCase();
|
||||||
progressIndicator.switchToIndeterminate(Bundle.Case_progressIndicatorStatus_openingCase());
|
theCase.doCaseAction(Bundle.Case_progressIndicatorTitle_deletingDataSource(), null, CaseLockType.EXCLUSIVE, false, dataSourceObjectID);
|
||||||
progressIndicator.start(Bundle.Case_progressIndicatorStatus_deletingDataSource());
|
openAsCurrentCase(theCase, false);
|
||||||
deleteDataSource(dataSourceId, progressIndicator, metadata);
|
|
||||||
progressIndicator.finish();
|
|
||||||
openAsCurrentCase(new Case(newMetadata), false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a data source from the current case.
|
|
||||||
*
|
|
||||||
* @param dataSourceId id of the data source to delete.
|
|
||||||
* @param progressIndicator
|
|
||||||
*/
|
|
||||||
@Messages({
|
|
||||||
"Case.DeletingDataSourceFromCase=Deleting the Data Source from the case.",
|
|
||||||
"Case.ErrorDeletingDataSource.name.text=Error Deleting Data Source"
|
|
||||||
})
|
|
||||||
static void deleteDataSource(Long dataSourceId, ProgressIndicator progressIndicator, CaseMetadata metadata) throws CaseActionException {
|
|
||||||
// get case actions lock
|
|
||||||
closeCurrentCase();
|
|
||||||
// If multi-user case, get exclusive lock on case, else continue w/o lock
|
|
||||||
Case theCase = new Case(metadata);
|
|
||||||
theCase.openCaseDataBase(progressIndicator);
|
|
||||||
theCase.openAppServiceCaseResources(progressIndicator);
|
|
||||||
try {
|
|
||||||
SleuthkitCaseAdmin.deleteDataSource(null, dataSourceId);
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
}
|
|
||||||
eventPublisher.publish(new DataSourceDeletedEvent(dataSourceId));
|
|
||||||
theCase.close(progressIndicator);
|
|
||||||
openAsCurrentCase(new Case(metadata), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a case. The case to be deleted must not be the "current case."
|
* Deletes a case. The case to be deleted must not be the "current case."
|
||||||
* Deleting the current case must be done by calling Case.deleteCurrentCase.
|
* Deleting the current case must be done by calling Case.deleteCurrentCase.
|
||||||
@ -870,6 +828,8 @@ public class Case {
|
|||||||
* @throws CaseActionCancelledException If creating the case is cancelled.
|
* @throws CaseActionCancelledException If creating the case is cancelled.
|
||||||
*/
|
*/
|
||||||
@Messages({
|
@Messages({
|
||||||
|
"Case.progressIndicatorTitle.creatingCase=Creating Case",
|
||||||
|
"Case.progressIndicatorTitle.openingCase=Opening Case",
|
||||||
"Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window"
|
"Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window"
|
||||||
})
|
})
|
||||||
private static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase) throws CaseActionException, CaseActionCancelledException {
|
private static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase) throws CaseActionException, CaseActionCancelledException {
|
||||||
@ -887,7 +847,16 @@ public class Case {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
logger.log(Level.INFO, "Opening {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
|
logger.log(Level.INFO, "Opening {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
|
||||||
newCurrentCase.open(isNewCase);
|
String progressIndicatorTitle;
|
||||||
|
CaseAction<ProgressIndicator, Object, Void> caseAction;
|
||||||
|
if (isNewCase) {
|
||||||
|
progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_creatingCase();
|
||||||
|
caseAction = newCurrentCase::createCase;
|
||||||
|
} else {
|
||||||
|
progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_openingCase();
|
||||||
|
caseAction = newCurrentCase::openCase;
|
||||||
|
}
|
||||||
|
newCurrentCase.doCaseAction(progressIndicatorTitle, caseAction, CaseLockType.SHARED, true, null);
|
||||||
currentCase = newCurrentCase;
|
currentCase = newCurrentCase;
|
||||||
logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
|
logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
|
||||||
if (RuntimeProperties.runningWithGUI()) {
|
if (RuntimeProperties.runningWithGUI()) {
|
||||||
@ -1553,18 +1522,6 @@ public class Case {
|
|||||||
eventPublisher.publish(new DataSourceNameChangedEvent(dataSource, newName));
|
eventPublisher.publish(new DataSourceNameChangedEvent(dataSource, newName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies case event subscribers that a data source has been deleted from
|
|
||||||
* the case database.
|
|
||||||
*
|
|
||||||
* This should not be called from the event dispatch thread (EDT)
|
|
||||||
*
|
|
||||||
* @param dataSourceId The object ID of the data source that was deleted.
|
|
||||||
*/
|
|
||||||
public void notifyDataSourceDeleted(Long dataSourceId) {
|
|
||||||
eventPublisher.publish(new DataSourceDeletedEvent(dataSourceId));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies case event subscribers that a content tag has been added.
|
* Notifies case event subscribers that a content tag has been added.
|
||||||
*
|
*
|
||||||
@ -1795,23 +1752,30 @@ public class Case {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens this case by creating a task running in the same non-UI thread that
|
* Performs a case action by creating a task running in the same non-UI
|
||||||
* will be used to close the case. If the case is a single-user case, this
|
* thread that will be used to close the case. For both single-user and
|
||||||
* supports cancelling creation of the case by cancelling the task. If the
|
* mulit-user cases, this supports cancelling the case action by cancelling
|
||||||
* case is a multi-user case, this ensures ensures that case directory lock
|
* the task. If the case is a multi-user case, this also ensures that the
|
||||||
* held as long as the case is open is released in the same thread in which
|
* case lock is released in the same thread in which it was acquired, which
|
||||||
* it was acquired, as is required by the coordination service.
|
* is required by the coordination service.
|
||||||
*
|
*
|
||||||
* @param isNewCase True for a new case, false otherwise.
|
* @param progressIndicatorTitle A title for the progress indicator for the
|
||||||
|
* case action.
|
||||||
|
* @param caseAction The case action method.
|
||||||
|
* @param caseLockType The type of case lock required for the case
|
||||||
|
* action.
|
||||||
|
* @param allowCancellation Whether or not to allow the action to be
|
||||||
|
* cancelled.
|
||||||
|
* @param additionalParams An Object that holds any additional
|
||||||
|
* parameters for a case action. For this
|
||||||
|
* action, this is null.
|
||||||
*
|
*
|
||||||
* @throws CaseActionException If there is a problem creating the case. The
|
* @throws CaseActionException If there is a problem completing the action.
|
||||||
* exception will have a user-friendly message
|
* The exception will have a user-friendly
|
||||||
* and may be a wrapper for a lower-level
|
* message and may be a wrapper for a
|
||||||
* exception.
|
* lower-level exception.
|
||||||
*/
|
*/
|
||||||
@Messages({
|
@Messages({
|
||||||
"Case.progressIndicatorTitle.creatingCase=Creating Case",
|
|
||||||
"Case.progressIndicatorTitle.openingCase=Opening Case",
|
|
||||||
"Case.progressIndicatorCancelButton.label=Cancel",
|
"Case.progressIndicatorCancelButton.label=Cancel",
|
||||||
"Case.progressMessage.preparing=Preparing...",
|
"Case.progressMessage.preparing=Preparing...",
|
||||||
"Case.progressMessage.preparingToOpenCaseResources=<html>Preparing to open case resources.<br>This may take time if another user is upgrading the case.</html>",
|
"Case.progressMessage.preparingToOpenCaseResources=<html>Preparing to open case resources.<br>This may take time if another user is upgrading the case.</html>",
|
||||||
@ -1819,62 +1783,66 @@ public class Case {
|
|||||||
"Case.exceptionMessage.cancelledByUser=Cancelled by user.",
|
"Case.exceptionMessage.cancelledByUser=Cancelled by user.",
|
||||||
"# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}"
|
"# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}"
|
||||||
})
|
})
|
||||||
private void open(boolean isNewCase) throws CaseActionException {
|
private void doCaseAction(String progressIndicatorTitle, CaseAction<ProgressIndicator, Object, Void> caseAction, CaseLockType caseLockType, boolean allowCancellation, Object additionalParams) throws CaseActionException {
|
||||||
/*
|
/*
|
||||||
* Create and start either a GUI progress indicator with a Cancel button
|
* Create and start either a GUI progress indicator or a logging
|
||||||
* or a logging progress indicator.
|
* progress indicator.
|
||||||
*/
|
*/
|
||||||
CancelButtonListener cancelButtonListener = null;
|
CancelButtonListener cancelButtonListener = null;
|
||||||
ProgressIndicator progressIndicator;
|
ProgressIndicator progressIndicator;
|
||||||
if (RuntimeProperties.runningWithGUI()) {
|
if (RuntimeProperties.runningWithGUI()) {
|
||||||
cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling());
|
if (allowCancellation) {
|
||||||
String progressIndicatorTitle = isNewCase ? Bundle.Case_progressIndicatorTitle_creatingCase() : Bundle.Case_progressIndicatorTitle_openingCase();
|
cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling());
|
||||||
progressIndicator = new ModalDialogProgressIndicator(
|
progressIndicator = new ModalDialogProgressIndicator(
|
||||||
mainFrame,
|
mainFrame,
|
||||||
progressIndicatorTitle,
|
progressIndicatorTitle,
|
||||||
new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
|
new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
|
||||||
Bundle.Case_progressIndicatorCancelButton_label(),
|
Bundle.Case_progressIndicatorCancelButton_label(),
|
||||||
cancelButtonListener);
|
cancelButtonListener);
|
||||||
|
} else {
|
||||||
|
progressIndicator = new ModalDialogProgressIndicator(
|
||||||
|
mainFrame,
|
||||||
|
progressIndicatorTitle);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
progressIndicator = new LoggingProgressIndicator();
|
progressIndicator = new LoggingProgressIndicator();
|
||||||
}
|
}
|
||||||
progressIndicator.start(Bundle.Case_progressMessage_preparing());
|
progressIndicator.start(Bundle.Case_progressMessage_preparing());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Creating/opening a case is always done by creating a task running in
|
* A case action is always done by creating a task running in the same
|
||||||
* the same non-UI thread that will be used to close the case, so a
|
* non-UI thread that will be used to close the case, so a
|
||||||
* single-threaded executor service is created here and saved as case
|
* single-threaded executor service is created here and saved as case
|
||||||
* state (must be volatile for cancellation to work).
|
* state (must be volatile for cancellation to work).
|
||||||
*
|
*
|
||||||
* --- If the case is a single-user case, this supports cancelling
|
* --- If the case is a single-user case, this supports cancelling the
|
||||||
* opening of the case by cancelling the task.
|
* case action by cancelling the task.
|
||||||
*
|
*
|
||||||
* --- If the case is a multi-user case, this still supports
|
* --- If the case is a multi-user case, this still supports
|
||||||
* cancellation, but it also makes it possible for the shared case
|
* cancellation, but it also makes it possible for the case lock held as
|
||||||
* directory lock held as long as the case is open to be released in the
|
* long as the case is open to be released in the same thread in which
|
||||||
* same thread in which it was acquired, as is required by the
|
* it was acquired, as is required by the coordination service.
|
||||||
* coordination service.
|
|
||||||
*/
|
*/
|
||||||
TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName()));
|
TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName()));
|
||||||
caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory);
|
caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory);
|
||||||
Future<Void> future = caseLockingExecutor.submit(() -> {
|
Future<Void> future = caseLockingExecutor.submit(() -> {
|
||||||
if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
|
if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
|
||||||
open(isNewCase, progressIndicator);
|
caseAction.execute(progressIndicator, additionalParams);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* First, acquire a shared case directory lock that will be held
|
* First, acquire a case lock that will be held as long as this
|
||||||
* as long as this node has this case open. This will prevent
|
* node has this case open. This will prevent deletion of the
|
||||||
* deletion of the case by another node. Next, acquire an
|
* case by another node. Next, acquire an exclusive case
|
||||||
* exclusive case resources lock to ensure only one node at a
|
* resources lock to ensure only one node at a time can
|
||||||
* time can create/open/upgrade/close the case resources.
|
* create/open/upgrade/close the case resources.
|
||||||
*/
|
*/
|
||||||
progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources());
|
progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources());
|
||||||
acquireSharedCaseDirLock(metadata.getCaseDirectory());
|
acquireCaseLock(caseLockType, metadata.getCaseDirectory());
|
||||||
try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) {
|
try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) {
|
||||||
if (null == resourcesLock) {
|
if (null == resourcesLock) {
|
||||||
throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
|
throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
|
||||||
}
|
}
|
||||||
open(isNewCase, progressIndicator);
|
caseAction.execute(progressIndicator, additionalParams);
|
||||||
} catch (CaseActionException ex) {
|
} catch (CaseActionException ex) {
|
||||||
releaseSharedCaseDirLock(getMetadata().getCaseDirectory());
|
releaseSharedCaseDirLock(getMetadata().getCaseDirectory());
|
||||||
throw ex;
|
throw ex;
|
||||||
@ -1887,18 +1855,17 @@ public class Case {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wait for the case creation/opening task to finish.
|
* Wait for the case action task to finish.
|
||||||
*/
|
*/
|
||||||
try {
|
try {
|
||||||
future.get();
|
future.get();
|
||||||
} catch (InterruptedException discarded) {
|
} catch (InterruptedException discarded) {
|
||||||
/*
|
/*
|
||||||
* The thread this method is running in has been interrupted. Cancel
|
* The thread this method is running in has been interrupted. Cancel
|
||||||
* the create/open task, wait for it to finish, and shut down the
|
* the case action task, wait for it to finish, and shut down the
|
||||||
* executor. This can be done safely because if the task is
|
* executor. This can be done safely because if the task is
|
||||||
* completed with a cancellation condition, the case will have been
|
* completed with a cancellation condition, the case will have been
|
||||||
* closed and the case directory lock released will have been
|
* closed and the case lock will have been released.
|
||||||
* released.
|
|
||||||
*/
|
*/
|
||||||
if (null != cancelButtonListener) {
|
if (null != cancelButtonListener) {
|
||||||
cancelButtonListener.actionPerformed(null);
|
cancelButtonListener.actionPerformed(null);
|
||||||
@ -1908,21 +1875,20 @@ public class Case {
|
|||||||
ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
|
ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
|
||||||
} catch (CancellationException discarded) {
|
} catch (CancellationException discarded) {
|
||||||
/*
|
/*
|
||||||
* The create/open task has been cancelled. Wait for it to finish,
|
* The case action task has been cancelled. Wait for it to finish,
|
||||||
* and shut down the executor. This can be done safely because if
|
* and shut down the executor. This can be done safely because if
|
||||||
* the task is completed with a cancellation condition, the case
|
* the task is completed with a cancellation condition, the case
|
||||||
* will have been closed and the case directory lock released will
|
* will have been closed and the case lock will have been released.
|
||||||
* have been released.
|
|
||||||
*/
|
*/
|
||||||
ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
|
ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
|
||||||
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
|
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
|
||||||
} catch (ExecutionException ex) {
|
} catch (ExecutionException ex) {
|
||||||
/*
|
/*
|
||||||
* The create/open task has thrown an exception. Wait for it to
|
* The case action task has thrown an exception. Wait for it to
|
||||||
* finish, and shut down the executor. This can be done safely
|
* finish, and shut down the executor. This can be done safely
|
||||||
* because if the task is completed with an execution condition, the
|
* because if the task is completed with an execution condition, the
|
||||||
* case will have been closed and the case directory lock released
|
* case will have been closed and the case lock will have been
|
||||||
* will have been released.
|
* released.
|
||||||
*/
|
*/
|
||||||
ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
|
ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
|
||||||
throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
|
throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
|
||||||
@ -1932,48 +1898,41 @@ public class Case {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the case database and services for this case.
|
* A case action (interface CaseAction<T, V, R>) that creates the case
|
||||||
|
* directory and case database and opens the application services for this
|
||||||
|
* case.
|
||||||
*
|
*
|
||||||
* @param isNewCase True for a new case, false otherwise.
|
|
||||||
* @param progressIndicator A progress indicator.
|
* @param progressIndicator A progress indicator.
|
||||||
|
* @param additionalParams An Object that holds any additional parameters
|
||||||
|
* for a case action. For this action, this is
|
||||||
|
* null.
|
||||||
*
|
*
|
||||||
* @throws CaseActionException If there is a problem creating the case. The
|
* @throws CaseActionException If there is a problem completing the action.
|
||||||
* exception will have a user-friendly message
|
* The exception will have a user-friendly
|
||||||
* and may be a wrapper for a lower-level
|
* message and may be a wrapper for a
|
||||||
* exception.
|
* lower-level exception.
|
||||||
*/
|
*/
|
||||||
private void open(boolean isNewCase, ProgressIndicator progressIndicator) throws CaseActionException {
|
private Void createCase(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
|
||||||
|
assert (additionalParams == null);
|
||||||
try {
|
try {
|
||||||
checkForUserCancellation();
|
checkForUserCancellation();
|
||||||
createCaseDirectoryIfDoesNotExist(progressIndicator);
|
createCaseDirectoryIfDoesNotExist(progressIndicator);
|
||||||
checkForUserCancellation();
|
checkForUserCancellation();
|
||||||
switchLoggingToCaseLogsDirectory(progressIndicator);
|
switchLoggingToCaseLogsDirectory(progressIndicator);
|
||||||
checkForUserCancellation();
|
checkForUserCancellation();
|
||||||
if (isNewCase) {
|
saveCaseMetadataToFile(progressIndicator);
|
||||||
saveCaseMetadataToFile(progressIndicator);
|
|
||||||
}
|
|
||||||
checkForUserCancellation();
|
checkForUserCancellation();
|
||||||
if (isNewCase) {
|
createCaseNodeData(progressIndicator);
|
||||||
createCaseNodeData(progressIndicator);
|
|
||||||
} else {
|
|
||||||
updateCaseNodeData(progressIndicator);
|
|
||||||
}
|
|
||||||
checkForUserCancellation();
|
checkForUserCancellation();
|
||||||
if (!isNewCase) {
|
|
||||||
deleteTempfilesFromCaseDirectory(progressIndicator);
|
|
||||||
}
|
|
||||||
checkForUserCancellation();
|
checkForUserCancellation();
|
||||||
if (isNewCase) {
|
createCaseDatabase(progressIndicator);
|
||||||
createCaseDatabase(progressIndicator);
|
|
||||||
} else {
|
|
||||||
openCaseDataBase(progressIndicator);
|
|
||||||
}
|
|
||||||
checkForUserCancellation();
|
checkForUserCancellation();
|
||||||
openCaseLevelServices(progressIndicator);
|
openCaseLevelServices(progressIndicator);
|
||||||
checkForUserCancellation();
|
checkForUserCancellation();
|
||||||
openAppServiceCaseResources(progressIndicator);
|
openAppServiceCaseResources(progressIndicator);
|
||||||
checkForUserCancellation();
|
checkForUserCancellation();
|
||||||
openCommunicationChannels(progressIndicator);
|
openCommunicationChannels(progressIndicator);
|
||||||
|
return null;
|
||||||
|
|
||||||
} catch (CaseActionException ex) {
|
} catch (CaseActionException ex) {
|
||||||
/*
|
/*
|
||||||
@ -1991,6 +1950,96 @@ public class Case {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A case action (interface CaseAction<T, V, R>) that opens the case
|
||||||
|
* database and services for this case.
|
||||||
|
*
|
||||||
|
* @param progressIndicator A progress indicator.
|
||||||
|
* @param additionalParams An Object that holds any additional parameters
|
||||||
|
* for a case action. For this action, this is
|
||||||
|
* null.
|
||||||
|
*
|
||||||
|
* @throws CaseActionException If there is a problem completing the action.
|
||||||
|
* The exception will have a user-friendly
|
||||||
|
* message and may be a wrapper for a
|
||||||
|
* lower-level exception.
|
||||||
|
*/
|
||||||
|
private Void openCase(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
|
||||||
|
assert (additionalParams == null);
|
||||||
|
try {
|
||||||
|
checkForUserCancellation();
|
||||||
|
switchLoggingToCaseLogsDirectory(progressIndicator);
|
||||||
|
checkForUserCancellation();
|
||||||
|
updateCaseNodeData(progressIndicator);
|
||||||
|
checkForUserCancellation();
|
||||||
|
deleteTempfilesFromCaseDirectory(progressIndicator);
|
||||||
|
checkForUserCancellation();
|
||||||
|
openCaseDataBase(progressIndicator);
|
||||||
|
checkForUserCancellation();
|
||||||
|
openCaseLevelServices(progressIndicator);
|
||||||
|
checkForUserCancellation();
|
||||||
|
openAppServiceCaseResources(progressIndicator);
|
||||||
|
checkForUserCancellation();
|
||||||
|
openCommunicationChannels(progressIndicator);
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} catch (CaseActionException ex) {
|
||||||
|
/*
|
||||||
|
* Cancellation or failure. Clean up by calling the close method.
|
||||||
|
* The sleep is a little hack to clear the interrupted flag for this
|
||||||
|
* thread if this is a cancellation scenario, so that the clean up
|
||||||
|
* can run to completion in the current thread.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
Thread.sleep(1);
|
||||||
|
} catch (InterruptedException discarded) {
|
||||||
|
}
|
||||||
|
close(progressIndicator);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A case action (interface CaseAction<T, V, R>) for a closed case that
|
||||||
|
* opens the case database and application services for this case, deletes a
|
||||||
|
* data source from the case, and publishes an application event indicasting
|
||||||
|
* the data source has been deleted.
|
||||||
|
*
|
||||||
|
* Note that this case action does not support cancellation.
|
||||||
|
*
|
||||||
|
* @param progressIndicator A progress indicator.
|
||||||
|
* @param additionalParams An Object that holds any additional parameters
|
||||||
|
* for a case action. For this action, this the
|
||||||
|
* object ID of the data source to be deleted.
|
||||||
|
*
|
||||||
|
* @throws CaseActionException If there is a problem completing the action.
|
||||||
|
* The exception will have a user-friendly
|
||||||
|
* message and may be a wrapper for a
|
||||||
|
* lower-level exception.
|
||||||
|
*/
|
||||||
|
@Messages({
|
||||||
|
"Case.DeletingDataSourceFromCase=Deleting the Data Source from the case.",
|
||||||
|
"# {0} - exception message", "Case.exceptionMessage.errorDeletingDataSource=An error occurred while deleting the data source:\n{0}."
|
||||||
|
})
|
||||||
|
Void deleteDataSource(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
|
||||||
|
assert (additionalParams instanceof Long);
|
||||||
|
openCaseDataBase(progressIndicator);
|
||||||
|
openAppServiceCaseResources(progressIndicator);
|
||||||
|
Long dataSourceObjectID = (Long) additionalParams;
|
||||||
|
try {
|
||||||
|
SleuthkitCaseAdmin.deleteDataSource(this.caseDb, dataSourceObjectID);
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSource(ex.getMessage()), ex);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.caseServices.getKeywordSearchService().deleteDataSource(dataSourceObjectID);
|
||||||
|
} catch (KeywordSearchServiceException ex) {
|
||||||
|
throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSource(ex.getMessage()), ex);
|
||||||
|
}
|
||||||
|
eventPublisher.publish(new DataSourceDeletedEvent(dataSourceObjectID));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an empty portable case from the current case
|
* Create an empty portable case from the current case
|
||||||
*
|
*
|
||||||
@ -2475,7 +2524,7 @@ public class Case {
|
|||||||
/*
|
/*
|
||||||
* The wait has been interrupted by interrupting the thread running
|
* The wait has been interrupted by interrupting the thread running
|
||||||
* this method. Not allowing cancellation of case closing, so ignore
|
* this method. Not allowing cancellation of case closing, so ignore
|
||||||
* the interrupt. Likewsie, cancellation of the case closing task is
|
* the interrupt. Likewise, cancellation of the case closing task is
|
||||||
* not supported.
|
* not supported.
|
||||||
*/
|
*/
|
||||||
} catch (ExecutionException ex) {
|
} catch (ExecutionException ex) {
|
||||||
@ -2595,9 +2644,13 @@ public class Case {
|
|||||||
* cannot be acquired.
|
* cannot be acquired.
|
||||||
*/
|
*/
|
||||||
@Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory"})
|
@Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory"})
|
||||||
private void acquireSharedCaseDirLock(String caseDir) throws CaseActionException {
|
private void acquireCaseLock(CaseLockType lockType, String caseDir) throws CaseActionException {
|
||||||
try {
|
try {
|
||||||
caseDirLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS);
|
boolean flag = true;
|
||||||
|
CoordinationService coordinationService = CoordinationService.getInstance();
|
||||||
|
caseDirLock = lockType == CaseLockType.SHARED
|
||||||
|
? coordinationService.tryGetSharedLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS)
|
||||||
|
: coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS);
|
||||||
if (null == caseDirLock) {
|
if (null == caseDirLock) {
|
||||||
throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock());
|
throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock());
|
||||||
}
|
}
|
||||||
@ -2994,6 +3047,15 @@ public class Case {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface CaseAction<T, V, R> {
|
||||||
|
|
||||||
|
R execute(T t, V v) throws CaseActionException;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum CaseLockType {
|
||||||
|
SHARED, EXCLUSIVE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A case operation Cancel button listener for use with a
|
* A case operation Cancel button listener for use with a
|
||||||
* ModalDialogProgressIndicator when running with a GUI.
|
* ModalDialogProgressIndicator when running with a GUI.
|
||||||
|
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2019 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;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.SwingWorker;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Action that allows a user to delete a data source from the current case.
|
||||||
|
*/
|
||||||
|
public final class DeleteDataSourceAction extends AbstractAction {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName());
|
||||||
|
private long dataSourceObjectID;
|
||||||
|
private Path caseMetadataFilePath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an Action that allows a user to delete a data source.
|
||||||
|
*
|
||||||
|
* @param dataSourceObjectID The object ID of the data source to be deleted.
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"DeleteDataSourceAction.name.text=Delete Data Source"
|
||||||
|
})
|
||||||
|
public DeleteDataSourceAction(Long dataSourceObjectID) {
|
||||||
|
super(Bundle.DeleteDataSourceAction_name_text());
|
||||||
|
this.dataSourceObjectID = dataSourceObjectID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?",
|
||||||
|
"DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source.\nPlease see the application log for details.",
|
||||||
|
"DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to reopen the case.",})
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent event) {
|
||||||
|
if (MessageNotifyUtil.Message.confirm(Bundle.DeleteDataSourceAction_confirmationDialog_message())) {
|
||||||
|
new SwingWorker<Void, Void>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground() throws Exception {
|
||||||
|
caseMetadataFilePath = Case.getCurrentCase().getMetadata().getFilePath();
|
||||||
|
/*
|
||||||
|
* Note that the case is closed and re-opened by this case
|
||||||
|
* action.
|
||||||
|
*/
|
||||||
|
Case.deleteDataSourceFromCurrentCase(dataSourceObjectID);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void done() {
|
||||||
|
try {
|
||||||
|
get();
|
||||||
|
} catch (InterruptedException | ExecutionException ex) {
|
||||||
|
logger.log(Level.SEVERE, String.format("Error deleting data source (obj_id=%d)", dataSourceObjectID), ex);
|
||||||
|
MessageNotifyUtil.Message.show(Bundle.DeleteDataSourceAction_exceptionMessage_dataSourceDeletionError(), MessageNotifyUtil.MessageType.ERROR);
|
||||||
|
if (!Case.isCaseOpen()) {
|
||||||
|
try {
|
||||||
|
Case.openAsCurrentCase(caseMetadataFilePath.toString());
|
||||||
|
} catch (CaseActionException ex2) {
|
||||||
|
logger.log(Level.SEVERE, "Failed to reopen the case after data source deletion error", ex2);
|
||||||
|
MessageNotifyUtil.Message.show(Bundle.DeleteDataSourceAction_exceptionMessage_couldNotReopenCase(), MessageNotifyUtil.MessageType.ERROR);
|
||||||
|
StartupWindowProvider.getInstance().open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DeleteDataSourceAction clone() throws CloneNotSupportedException {
|
||||||
|
DeleteDataSourceAction clonedObject = ((DeleteDataSourceAction) super.clone());
|
||||||
|
clonedObject.setDataSourceID(this.dataSourceObjectID);
|
||||||
|
return clonedObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDataSourceID(long dataSourceID) {
|
||||||
|
this.dataSourceObjectID = dataSourceID;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.casemodule;
|
package org.sleuthkit.autopsy.casemodule;
|
||||||
|
|
||||||
|
import org.sleuthkit.autopsy.access.AccessLimiterUtils;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2019 Basis Technology Corp.
|
* Copyright 2012-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -20,8 +20,6 @@ package org.sleuthkit.autopsy.datamodel;
|
|||||||
|
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.File;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -35,14 +33,14 @@ import org.apache.commons.lang3.tuple.Pair;
|
|||||||
import org.openide.nodes.Sheet;
|
import org.openide.nodes.Sheet;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.actions.DeleteDataSourceAction;
|
import org.sleuthkit.autopsy.access.AccessLimiterUtils;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.DeleteDataSourceAction;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction;
|
import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction;
|
||||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
|
||||||
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
|
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
|
||||||
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
|
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
|
||||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||||
@ -66,10 +64,6 @@ public class ImageNode extends AbstractContentNode<Image> {
|
|||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(ImageNode.class.getName());
|
private static final Logger logger = Logger.getLogger(ImageNode.class.getName());
|
||||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.CONTENT_CHANGED);
|
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.CONTENT_CHANGED);
|
||||||
private final static String ADMIN_ACCESS_FILE_NAME = "admin"; // NON-NLS
|
|
||||||
private final static String ADMIN_ACCESS_FILE_PATH = Paths.get(PlatformUtil.getUserConfigDirectory(), ADMIN_ACCESS_FILE_NAME).toString();
|
|
||||||
private final static String ADMIN_EXT_ACCESS_FILE_NAME = "adminext"; // NON-NLS
|
|
||||||
private final static String ADMIN_EXT_ACCESS_FILE_PATH = Paths.get(PlatformUtil.getUserConfigDirectory(), ADMIN_EXT_ACCESS_FILE_NAME).toString();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper so that the display name and the name used in building the path
|
* Helper so that the display name and the name used in building the path
|
||||||
@ -128,7 +122,7 @@ public class ImageNode extends AbstractContentNode<Image> {
|
|||||||
actionsList.add(new RunIngestModulesAction(Collections.<Content>singletonList(content)));
|
actionsList.add(new RunIngestModulesAction(Collections.<Content>singletonList(content)));
|
||||||
actionsList.add(new NewWindowViewAction(
|
actionsList.add(new NewWindowViewAction(
|
||||||
NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this));
|
NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this));
|
||||||
if (checkSchemaVersion() && checkMuAdmin()) {
|
if (canAddDeleteDataSourceAction()) {
|
||||||
actionsList.add(new DeleteDataSourceAction(content.getId()));
|
actionsList.add(new DeleteDataSourceAction(content.getId()));
|
||||||
}
|
}
|
||||||
return actionsList.toArray(new Action[0]);
|
return actionsList.toArray(new Action[0]);
|
||||||
@ -218,29 +212,17 @@ public class ImageNode extends AbstractContentNode<Image> {
|
|||||||
return getClass().getName();
|
return getClass().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Boolean checkSchemaVersion() {
|
/**
|
||||||
try {
|
* Determines whether or not the delete data source action can be added.
|
||||||
CaseDbSchemaVersionNumber creationVersion = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaCreationVersion();
|
* @return True or false.
|
||||||
|
*/
|
||||||
if ((creationVersion.getMajor() == 8 && creationVersion.getMinor() >= 3) || creationVersion.getMajor() > 8) {
|
private Boolean canAddDeleteDataSourceAction() {
|
||||||
return true;
|
boolean canAddAction = false;
|
||||||
}
|
CaseDbSchemaVersionNumber creationVersion = Case.getCurrentCase().getSleuthkitCase().getDBSchemaCreationVersion();
|
||||||
} catch (NoCurrentCaseException ex) {
|
if ((creationVersion.getMajor() == 8 && creationVersion.getMinor() >= 3) || (creationVersion.getMajor() > 8)) {
|
||||||
logger.log(Level.WARNING, "Failed to get creation schema version: ", ex);
|
canAddAction = Case.getCurrentCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE || !AccessLimiterUtils.limitMultiUserAccess();
|
||||||
}
|
}
|
||||||
|
return canAddAction;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Boolean checkMuAdmin() {
|
|
||||||
try {
|
|
||||||
if (Case.CaseType.MULTI_USER_CASE == Case.getCurrentCaseThrows().getCaseType()) {
|
|
||||||
return new File(ADMIN_ACCESS_FILE_PATH).exists() || new File(ADMIN_EXT_ACCESS_FILE_PATH).exists();
|
|
||||||
}
|
|
||||||
} catch (NoCurrentCaseException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Failed to get the Create Major and Minor Schema Versions", ex);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user