From 7d6fb729ef6d831f0a359d579cde0bba7d088219 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Wed, 29 May 2019 11:26:02 -0400 Subject: [PATCH 01/37] Add User Feedback about Deleting Data Source Add notify box asking you if they really want to delete the data source. --- .../autopsy/actions/Bundle.properties-MERGED | 2 + .../actions/DeleteDataSourceAction.java | 41 ++++++++++++------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED index 757d2db120..b19a62b5c0 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED @@ -24,6 +24,8 @@ DeleteContentTagAction.tagDelErr=Tag Deletion Error # {0} - tagName DeleteContentTagAction.unableToDelTag.msg=Unable to delete tag {0}. DeleteDataSourceAction.name.text=Delete Data Source +DeleteDataSourceConfirmationDialog_message=Are you sure you want to delete the data source? +DeleteDataSourceConfirmationDialog_title=Delete Data Source? DeleteFileBlackboardArtifactTagAction.deleteTag=Remove Result Tag # {0} - artifactID DeleteFileBlackboardArtifactTagAction.deleteTag.alert=Unable to untag artifact {0}. diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index 4557cddaef..30b621b7e4 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -22,10 +22,12 @@ import java.awt.event.ActionEvent; import java.text.MessageFormat; import java.util.logging.Level; import javax.swing.AbstractAction; +import org.openide.DialogDescriptor; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.CaseMetadata; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; @@ -33,10 +35,7 @@ import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException; import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.datamodel.TskCoreException; -/** - * - * @author markm - */ + public final class DeleteDataSourceAction extends AbstractAction { private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName()); private final Long selectDataSource; @@ -45,21 +44,33 @@ public final class DeleteDataSourceAction extends AbstractAction { public DeleteDataSourceAction(Long selectedDataSource) { super(Bundle.DeleteDataSourceAction_name_text()); selectDataSource = selectedDataSource; + } - @NbBundle.Messages({"ErrorDeletingDataSource.name.text=Error Deleting Data Source"}) + @NbBundle.Messages({"ErrorDeletingDataSource.name.text=Error Deleting Data Source", + "DeleteDataSourceConfirmationDialog_message=Are you sure you want to delete the data source?", + "DeleteDataSourceConfirmationDialog_title=Delete Data Source?"}) @Override public void actionPerformed(ActionEvent event) { - try { - //VersionNumber checkVersionNumber = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaVersion(); - Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectDataSource); - deleteDataSource(selectDataSource); - } catch (NoCurrentCaseException | TskCoreException | KeywordSearchServiceException e) { - String msg = MessageFormat.format(Bundle.ErrorDeletingDataSource_name_text(), selectDataSource); - logger.log(Level.WARNING, msg, e); - //throw new TskCoreException(msg, e); + Object response = DialogDisplayer.getDefault().notify(new NotifyDescriptor( + Bundle.DeleteDataSourceConfirmationDialog_message(), + Bundle.DeleteDataSourceConfirmationDialog_title(), + NotifyDescriptor.YES_NO_OPTION, + NotifyDescriptor.WARNING_MESSAGE, + null, + NotifyDescriptor.NO_OPTION)); + if (null != response && DialogDescriptor.YES_OPTION == response) { + try { + Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectDataSource); + deleteDataSource(selectDataSource); + } catch (NoCurrentCaseException | TskCoreException | KeywordSearchServiceException e) { + String msg = MessageFormat.format(Bundle.ErrorDeletingDataSource_name_text(), selectDataSource); + logger.log(Level.WARNING, msg, e); + //throw new TskCoreException(msg, e); + } } - } + } + private static void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException { try { KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); From bd18de5086b6b2e416b1c9fc31f5eac37af00513 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Wed, 5 Jun 2019 17:08:25 -0400 Subject: [PATCH 02/37] Initial commit of code Initial commit of code --- .../actions/DeleteDataSourceAction.java | 38 ++++++---- .../casemodule/Bundle.properties-MERGED | 6 ++ .../sleuthkit/autopsy/casemodule/Case.java | 70 +++++++++++++++++++ .../events/DataSourceDeletedEvent.java | 51 ++++++++++++++ 4 files changed, 152 insertions(+), 13 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index 30b621b7e4..47ff5e4b4c 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -20,20 +20,21 @@ package org.sleuthkit.autopsy.actions; import java.awt.event.ActionEvent; import java.text.MessageFormat; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.AbstractAction; +import javax.swing.JOptionPane; +import javax.swing.SwingWorker; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; 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.casemodule.CaseActionException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException; -import org.sleuthkit.autopsy.progress.ProgressIndicator; -import org.sleuthkit.datamodel.TskCoreException; public final class DeleteDataSourceAction extends AbstractAction { @@ -60,14 +61,25 @@ public final class DeleteDataSourceAction extends AbstractAction { null, NotifyDescriptor.NO_OPTION)); if (null != response && DialogDescriptor.YES_OPTION == response) { - try { - Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectDataSource); - deleteDataSource(selectDataSource); - } catch (NoCurrentCaseException | TskCoreException | KeywordSearchServiceException e) { - String msg = MessageFormat.format(Bundle.ErrorDeletingDataSource_name_text(), selectDataSource); - logger.log(Level.WARNING, msg, e); - //throw new TskCoreException(msg, e); - } + + new SwingWorker() { + + @Override + protected Void doInBackground() throws Exception { + try { + Case.deleteDataSourceFromCurrentCase(selectDataSource); + deleteDataSource(selectDataSource); + } catch (CaseActionException | KeywordSearchServiceException ex) { + String msg = MessageFormat.format(Bundle.ErrorDeletingDataSource_name_text(), selectDataSource); + logger.log(Level.WARNING, msg, ex); + } + return null; + } + + @Override + protected void done() { + } + }.execute(); } } @@ -75,8 +87,8 @@ public final class DeleteDataSourceAction extends AbstractAction { try { KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); kwsService.deleteDataSource(dataSourceId); - } catch (KeywordSearchServiceException e) { - logger.log(Level.WARNING, "KWS Error", e); + } catch (KeywordSearchServiceException ex) { + logger.log(Level.WARNING, "KWS Error", ex); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 4150e5729e..e29ab171d7 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -9,6 +9,8 @@ Case.deleteCaseConfirmationDialog.title=Delete Current Case? # {0} - exception message Case.deleteCaseFailureMessageBox.message=Error deleting case: {0} Case.deleteCaseFailureMessageBox.title=Failed to Delete 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.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. @@ -52,10 +54,14 @@ Case.exceptionMessage.metadataUpdateError=Failed to update case metadata 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.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.creatingCase=Creating Case Case.progressIndicatorTitle.deletingCase=Deleting Case Case.progressIndicatorTitle.openingCase=Opening Case +Case.progressIndicatorTitle_deletingDataSource=Deleting Data Source from the case. Case.progressMessage.cancelling=Cancelling... Case.progressMessage.clearingTempDirectory=Clearing case temp directory... Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources... diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index c9640f1a13..08499f602a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -79,6 +79,7 @@ import org.sleuthkit.autopsy.casemodule.events.CommentChangedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent; +import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceNameChangedEvent; import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent; import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException; @@ -702,6 +703,75 @@ public class Case { } } + /** + * Deletes the selected data source. + * + * @param dataSourceId id of the data source to delete. + * @throws CaseActionException If there is a problem deleting the case. The + * exception will have a user-friendly message + * and may be a wrapper for a lower-level + * exception. + */ + @Messages({ + "Case.progressIndicatorTitle_deletingDataSource=Deleting Data Source from the case.", + "Case.progressIndicatorStatus_closingCase=Closing Case to Deleting Data Source.", + "Case.progressIndicatorStatus_openingCase=Opening Case to Deleting Data Source.", + "Case.progressIndicatorStatus_deletingDataSource=Deleting Data Source.", + }) + public static void deleteDataSourceFromCurrentCase(Long dataSourceId) throws CaseActionException { + synchronized (caseActionSerializationLock) { + if (null == currentCase) { + return; + } + CaseMetadata newMetadata = null; + 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(); + } + progressIndicator.start(Bundle.Case_progressIndicatorStatus_closingCase()); + closeCurrentCase(); + progressIndicator.progress(Bundle.Case_progressIndicatorStatus_openingCase()); + openAsCurrentCase(new Case(newMetadata), false); + progressIndicator.progress(Bundle.Case_progressIndicatorStatus_deletingDataSource()); + deleteDataSource(dataSourceId, progressIndicator); + progressIndicator.start(Bundle.Case_progressIndicatorStatus_closingCase()); + closeCurrentCase(); + progressIndicator.progress(Bundle.Case_progressIndicatorStatus_openingCase()); + openAsCurrentCase(new Case(newMetadata), false); + progressIndicator.finish(); + } + + } + + /** + * 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" + }) + private static void deleteDataSource(Long dataSourceId, ProgressIndicator progressIndicator) { + progressIndicator.progress(Bundle.Case_DeletingDataSourceFromCase()); + try { + Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(dataSourceId); + eventPublisher.publish(new DataSourceDeletedEvent(dataSourceId)); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.WARNING, String.format("Error Deleting Data Source %s", dataSourceId), ex); + } + } + /** * 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. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java new file mode 100644 index 0000000000..5317a951d0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java @@ -0,0 +1,51 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2015-2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sleuthkit.autopsy.casemodule.events; + +import java.io.Serializable; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.events.AutopsyEvent; + +public class DataSourceDeletedEvent extends AutopsyEvent implements Serializable { + + private static final long serialVersionUID = 1L; + private final long dataSourceId; + + /** + * Constructs an event published when a data source is added to a case. + * + * @param dataSourceId The data source that was deleted. + */ + public DataSourceDeletedEvent(Long dataSourceId) { + + super(Case.Events.DATA_SOURCE_DELETED.toString(), null, dataSourceId); + this.dataSourceId = dataSourceId; + } + + /** + * Gets the data source id that is being deleted + * + * @return The data source id. + */ + public long getDataSourceId() { + return dataSourceId; + } + +} From 1c345f819f10db7be973f29bbed064e3ae933ce8 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 11 Jun 2019 15:49:12 -0400 Subject: [PATCH 03/37] Intial code commit initial code commit for allowing a multi user case to only be deleted by admins --- .../autopsy/datamodel/ImageNode.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index d5d5d1bd04..39d9995625 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.datamodel; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.io.File; +import java.nio.file.Paths; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; @@ -34,9 +36,11 @@ import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.actions.DeleteDataSourceAction; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.CaseMetadata; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor; import org.sleuthkit.autopsy.directorytree.FileSearchAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; @@ -56,6 +60,10 @@ import org.sleuthkit.datamodel.VirtualDirectory; public class ImageNode extends AbstractContentNode { private static final Logger logger = Logger.getLogger(ImageNode.class.getName()); + 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 @@ -114,7 +122,7 @@ public class ImageNode extends AbstractContentNode { actionsList.add(new RunIngestModulesAction(Collections.singletonList(content))); actionsList.add(new NewWindowViewAction( NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this)); - if (checkSchemaVersion()) { + if (checkSchemaVersion() && checkMuAdmin()) { actionsList.add(new DeleteDataSourceAction(content.getId())); } return actionsList.toArray(new Action[0]); @@ -224,6 +232,17 @@ public class ImageNode extends AbstractContentNode { 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; + } /* * This property change listener refreshes the tree when a new file is From 0bebf66e1d2a2837c75007db904fbdc2f50b6cea Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 11 Jun 2019 16:07:10 -0400 Subject: [PATCH 04/37] Update ImageNode.java Codacy Fix --- Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 39d9995625..3f8ae2548d 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -36,7 +36,6 @@ import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.actions.DeleteDataSourceAction; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.CaseMetadata; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction; import org.sleuthkit.autopsy.coreutils.Logger; From 18c1a221f427c289adda76509cf4366bb07501d1 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Fri, 21 Jun 2019 11:25:00 -0400 Subject: [PATCH 05/37] Initial code Initial code to check for deleted data sources when the image gallery is started. --- .../imagegallery/datamodel/DrawableDB.java | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java index 037a2004a8..58a8682191 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java @@ -56,8 +56,10 @@ import javax.swing.SortOrder; import static org.apache.commons.lang3.ObjectUtils.notEqual; import org.apache.commons.lang3.StringUtils; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.DhsImageCategory; +import org.sleuthkit.autopsy.events.AutopsyEventPublisher; import org.sleuthkit.autopsy.imagegallery.FileTypeUtils; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; import org.sleuthkit.autopsy.imagegallery.ImageGalleryModule; @@ -230,6 +232,7 @@ public final class DrawableDB { dbWriteLock(); try { con = DriverManager.getConnection("jdbc:sqlite:" + dbPath.toString()); //NON-NLS +// if (!initializeDBSchema() || !upgradeDBSchema() || !prepareStatements() || !initializeStandardGroups() || !initializeImageList() || !checkDataSourceExistsInCase()) { if (!initializeDBSchema() || !upgradeDBSchema() || !prepareStatements() || !initializeStandardGroups() || !initializeImageList()) { close(); throw new TskCoreException("Failed to initialize drawables database for Image Gallery use"); //NON-NLS @@ -433,6 +436,36 @@ public final class DrawableDB { return tableExists; } + private Boolean checkDataSourceExistsInCase() throws SQLException { + try { + dbWriteLock(); + Map dataSourceIdsMap = getDataSourceDbBuildStatus(); + for (Map.Entry dataSourceMap : dataSourceIdsMap.entrySet()) { + String name + = "SELECT COUNT(obj_id) obj_id FROM tsk_objects where obj_id = " + Long.toString(dataSourceMap.getKey()); //NON-NLS + try { + SleuthkitCase.CaseDbQuery executeQuery = tskCase.executeQuery(name); + ResultSet resultSet = executeQuery.getResultSet(); + while (resultSet.next()) { + if (resultSet.getLong("obj_id") == 0) { + try (Statement stmt = con.createStatement()) { + stmt.execute("DELETE FROM datasources where ds_obj_id = " + Long.toString(dataSourceMap.getKey())); + } + } + } + resultSet.close(); + } catch (SQLException ex) { + logger.log(Level.INFO, String.format("Error trying to Delete Data source", ex)); //NON-NLS + } + } + } catch (TskCoreException ex) { + logger.log(Level.INFO, String.format("Error trying to Delete Data source", ex)); //NON-NLS + } finally { + dbWriteUnlock(); + return true; + } + } + private static void deleteDatabaseIfOlderVersion(Path dbPath) throws SQLException, IOException { if (Files.exists(dbPath)) { boolean hasDrawableFilesTable = false; @@ -2047,7 +2080,7 @@ public final class DrawableDB { commitTransaction(trans, true); } catch (SQLException | TskCoreException ex) { logger.log(Level.WARNING, "failed to deletesource for obj_id = " + dataSourceId, ex); //NON-NLS - } finally { + } finally { dbWriteUnlock(); } } From 7285f702c01eabb66ca14e36cfd6ba58bdbaeb8e Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 24 Jun 2019 15:10:01 -0400 Subject: [PATCH 06/37] Update Case.java Updated DeleteDatasourceId to use the new API code --- .../sleuthkit/autopsy/casemodule/Case.java | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 08499f602a..43760c7f38 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -122,6 +122,7 @@ import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Report; import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.SleuthkitCaseAdmin; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException; @@ -737,19 +738,13 @@ public class Case { } else { progressIndicator = new LoggingProgressIndicator(); } - progressIndicator.start(Bundle.Case_progressIndicatorStatus_closingCase()); closeCurrentCase(); - progressIndicator.progress(Bundle.Case_progressIndicatorStatus_openingCase()); - openAsCurrentCase(new Case(newMetadata), false); - progressIndicator.progress(Bundle.Case_progressIndicatorStatus_deletingDataSource()); - deleteDataSource(dataSourceId, progressIndicator); - progressIndicator.start(Bundle.Case_progressIndicatorStatus_closingCase()); - closeCurrentCase(); - progressIndicator.progress(Bundle.Case_progressIndicatorStatus_openingCase()); - openAsCurrentCase(new Case(newMetadata), false); + progressIndicator.switchToIndeterminate(Bundle.Case_progressIndicatorStatus_openingCase()); + progressIndicator.start(Bundle.Case_progressIndicatorStatus_deletingDataSource()); + deleteDataSource(dataSourceId, progressIndicator, metadata); progressIndicator.finish(); + openAsCurrentCase(new Case(newMetadata), false); } - } /** @@ -762,12 +757,22 @@ public class Case { "Case.DeletingDataSourceFromCase=Deleting the Data Source from the case.", "Case.ErrorDeletingDataSource.name.text=Error Deleting Data Source" }) - private static void deleteDataSource(Long dataSourceId, ProgressIndicator progressIndicator) { + private static void deleteDataSource(Long dataSourceId, ProgressIndicator progressIndicator, CaseMetadata metadata) { progressIndicator.progress(Bundle.Case_DeletingDataSourceFromCase()); +// CaseMetadata metadata = currentCase.getMetadata(); + SleuthkitCaseAdmin skCaseAdmin = null; try { - Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(dataSourceId); + if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { + String caseDbPath = metadata.getCaseDirectory() + File.separator + metadata.getCaseDatabaseName(); + skCaseAdmin = new SleuthkitCaseAdmin(caseDbPath); + } else { +// String caseName, CaseDbConnectionInfo info, String caseDirPath + CaseDbConnectionInfo caseDbConnectionInfo = UserPreferences.getDatabaseConnectionInfo(); + skCaseAdmin = new SleuthkitCaseAdmin(metadata.getCaseDatabaseName(), caseDbConnectionInfo, metadata.getCaseDirectory()); + } + skCaseAdmin.deleteDataSource(dataSourceId); eventPublisher.publish(new DataSourceDeletedEvent(dataSourceId)); - } catch (NoCurrentCaseException | TskCoreException ex) { + } catch (TskCoreException | UserPreferencesException ex) { logger.log(Level.WARNING, String.format("Error Deleting Data Source %s", dataSourceId), ex); } } From 97a3020d5631534b316a69aa0087c8509c761a1b Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 25 Jun 2019 22:09:07 -0400 Subject: [PATCH 07/37] Update Case.java --- .../sleuthkit/autopsy/casemodule/Case.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 43760c7f38..7664fd9e97 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -759,19 +759,26 @@ public class Case { }) private static void deleteDataSource(Long dataSourceId, ProgressIndicator progressIndicator, CaseMetadata metadata) { progressIndicator.progress(Bundle.Case_DeletingDataSourceFromCase()); -// CaseMetadata metadata = currentCase.getMetadata(); SleuthkitCaseAdmin skCaseAdmin = null; try { if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { String caseDbPath = metadata.getCaseDirectory() + File.separator + metadata.getCaseDatabaseName(); skCaseAdmin = new SleuthkitCaseAdmin(caseDbPath); } else { -// String caseName, CaseDbConnectionInfo info, String caseDirPath - CaseDbConnectionInfo caseDbConnectionInfo = UserPreferences.getDatabaseConnectionInfo(); - skCaseAdmin = new SleuthkitCaseAdmin(metadata.getCaseDatabaseName(), caseDbConnectionInfo, metadata.getCaseDirectory()); + try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) { + if (null == resourcesLock) { + throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock()); + } + CaseDbConnectionInfo caseDbConnectionInfo = UserPreferences.getDatabaseConnectionInfo(); + skCaseAdmin = new SleuthkitCaseAdmin(metadata.getCaseDatabaseName(), caseDbConnectionInfo, metadata.getCaseDirectory()); + } catch (CaseActionException | CoordinationServiceException ex) { + logger.log(Level.WARNING, String.format("Error acquiring MultiCase Lock"), ex); + } + } + if (skCaseAdmin != null) { + skCaseAdmin.deleteDataSource(dataSourceId); + eventPublisher.publish(new DataSourceDeletedEvent(dataSourceId)); } - skCaseAdmin.deleteDataSource(dataSourceId); - eventPublisher.publish(new DataSourceDeletedEvent(dataSourceId)); } catch (TskCoreException | UserPreferencesException ex) { logger.log(Level.WARNING, String.format("Error Deleting Data Source %s", dataSourceId), ex); } From 8db2acfe160dfe97a2ef0bf30fbdd7779ef47bd7 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 30 Oct 2019 16:42:20 -0400 Subject: [PATCH 08/37] integrate and update data src deletion --- .../AccessLimiterUtils.java | 6 +- .../autopsy/actions/Bundle.properties-MERGED | 1 - .../actions/DeleteDataSourceAction.java | 78 ---- .../casemodule/Bundle.properties-MERGED | 12 +- .../sleuthkit/autopsy/casemodule/Case.java | 344 +++++++++++------- .../casemodule/DeleteDataSourceAction.java | 107 ++++++ .../casemodule/NewCaseVisualPanel1.java | 1 + .../autopsy/datamodel/ImageNode.java | 48 +-- 8 files changed, 336 insertions(+), 261 deletions(-) rename Core/src/org/sleuthkit/autopsy/{casemodule => access}/AccessLimiterUtils.java (92%) delete mode 100644 Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AccessLimiterUtils.java b/Core/src/org/sleuthkit/autopsy/access/AccessLimiterUtils.java similarity index 92% rename from Core/src/org/sleuthkit/autopsy/casemodule/AccessLimiterUtils.java rename to Core/src/org/sleuthkit/autopsy/access/AccessLimiterUtils.java index 3b2a201e07..31f3a36bc8 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AccessLimiterUtils.java +++ b/Core/src/org/sleuthkit/autopsy/access/AccessLimiterUtils.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.casemodule; +package org.sleuthkit.autopsy.access; import java.io.File; 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 * */ -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_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. */ - static boolean limitMultiUserAccess() { + public static boolean limitMultiUserAccess() { return new File(MULTI_USER_ACCESS_FILE_PATH).exists(); } diff --git a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED index e6f0b377a2..a3a13c0cff 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED @@ -23,7 +23,6 @@ DeleteContentTagAction.deleteTag=Remove Selected Tag(s) DeleteContentTagAction.tagDelErr=Tag Deletion Error # {0} - tagName DeleteContentTagAction.unableToDelTag.msg=Unable to delete tag {0}. -DeleteDataSourceAction.name.text=Delete Data Source DeleteFileBlackboardArtifactTagAction.deleteTag=Remove Result Tag # {0} - artifactID DeleteFileBlackboardArtifactTagAction.deleteTag.alert=Unable to untag artifact {0}. diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java deleted file mode 100644 index b4c29f03ab..0000000000 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2019 Basis Technology Corp. - * Contact: carrier sleuthkit 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; - } - -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 0e16ccde21..824ee91474 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -10,7 +10,6 @@ Case.deleteCaseConfirmationDialog.title=Delete Current Case? Case.deleteCaseFailureMessageBox.message=Error deleting case: {0} Case.deleteCaseFailureMessageBox.title=Failed to Delete 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.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. @@ -38,6 +37,8 @@ Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination s Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled. Case.exceptionMessage.emptyCaseDir=Must specify a case directory path. 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. # {0} - exception message 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.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.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.creatingCase=Creating Case Case.progressIndicatorTitle.deletingCase=Deleting Case +Case.progressIndicatorTitle.deletingDataSource=Deleting Data Source Case.progressIndicatorTitle.openingCase=Opening Case -Case.progressIndicatorTitle_deletingDataSource=Deleting Data Source from the case. Case.progressMessage.cancelling=Cancelling... Case.progressMessage.clearingTempDirectory=Clearing case temp directory... Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources... @@ -124,6 +122,10 @@ CTL_CaseDetailsAction=Case Details CTL_CaseDeleteAction=Delete Case CTL_CaseOpenAction=Open 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.saveButton.text=Save GeneralFilter.encaseImageDesc.text=Encase Images (*.e01) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 80142dfbc5..715a4383b7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.casemodule; +import org.sleuthkit.autopsy.access.AccessLimiterUtils; import com.google.common.annotations.Beta; import com.google.common.eventbus.Subscribe; 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.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import java.util.logging.Level; import java.util.stream.Collectors; 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 * exception will have a user-friendly message @@ -751,64 +753,20 @@ public class Case { * exception. */ @Messages({ - "Case.progressIndicatorTitle_deletingDataSource=Deleting Data Source from the case.", - "Case.progressIndicatorStatus_closingCase=Closing Case to Deleting Data Source.", - "Case.progressIndicatorStatus_openingCase=Opening Case to Deleting Data Source.", - "Case.progressIndicatorStatus_deletingDataSource=Deleting Data Source.",}) - public static void deleteDataSourceFromCurrentCase(Long dataSourceId) throws CaseActionException { + "Case.progressIndicatorTitle.deletingDataSource=Deleting Data Source" + }) + public static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException { synchronized (caseActionSerializationLock) { if (null == currentCase) { return; } - CaseMetadata newMetadata = null; - 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(); - } + Case theCase = currentCase; closeCurrentCase(); - progressIndicator.switchToIndeterminate(Bundle.Case_progressIndicatorStatus_openingCase()); - progressIndicator.start(Bundle.Case_progressIndicatorStatus_deletingDataSource()); - deleteDataSource(dataSourceId, progressIndicator, metadata); - progressIndicator.finish(); - openAsCurrentCase(new Case(newMetadata), false); + theCase.doCaseAction(Bundle.Case_progressIndicatorTitle_deletingDataSource(), null, CaseLockType.EXCLUSIVE, false, dataSourceObjectID); + openAsCurrentCase(theCase, 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." * 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. */ @Messages({ + "Case.progressIndicatorTitle.creatingCase=Creating Case", + "Case.progressIndicatorTitle.openingCase=Opening Case", "Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window" }) private static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase) throws CaseActionException, CaseActionCancelledException { @@ -887,7 +847,16 @@ public class Case { } try { 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 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; 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()) { @@ -1553,18 +1522,6 @@ public class Case { 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. * @@ -1795,23 +1752,30 @@ public class Case { } /** - * Opens this case by creating a task running in the same non-UI thread that - * will be used to close the case. If the case is a single-user case, this - * supports cancelling creation of the case by cancelling the task. If the - * case is a multi-user case, this ensures ensures that case directory lock - * held as long as the case is open is released in the same thread in which - * it was acquired, as is required by the coordination service. + * Performs a case action by creating a task running in the same non-UI + * thread that will be used to close the case. For both single-user and + * mulit-user cases, this supports cancelling the case action by cancelling + * the task. If the case is a multi-user case, this also ensures that the + * case lock is released in the same thread in which it was acquired, which + * 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 - * exception will have a user-friendly message - * and may be a wrapper for a lower-level - * exception. + * @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.progressIndicatorTitle.creatingCase=Creating Case", - "Case.progressIndicatorTitle.openingCase=Opening Case", "Case.progressIndicatorCancelButton.label=Cancel", "Case.progressMessage.preparing=Preparing...", "Case.progressMessage.preparingToOpenCaseResources=Preparing to open case resources.
This may take time if another user is upgrading the case.", @@ -1819,62 +1783,66 @@ public class Case { "Case.exceptionMessage.cancelledByUser=Cancelled by user.", "# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}" }) - private void open(boolean isNewCase) throws CaseActionException { + private void doCaseAction(String progressIndicatorTitle, CaseAction caseAction, CaseLockType caseLockType, boolean allowCancellation, Object additionalParams) throws CaseActionException { /* - * Create and start either a GUI progress indicator with a Cancel button - * or a logging progress indicator. + * Create and start either a GUI progress indicator or a logging + * progress indicator. */ CancelButtonListener cancelButtonListener = null; ProgressIndicator progressIndicator; if (RuntimeProperties.runningWithGUI()) { - cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling()); - String progressIndicatorTitle = isNewCase ? Bundle.Case_progressIndicatorTitle_creatingCase() : Bundle.Case_progressIndicatorTitle_openingCase(); - progressIndicator = new ModalDialogProgressIndicator( - mainFrame, - progressIndicatorTitle, - new String[]{Bundle.Case_progressIndicatorCancelButton_label()}, - Bundle.Case_progressIndicatorCancelButton_label(), - cancelButtonListener); + if (allowCancellation) { + cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling()); + progressIndicator = new ModalDialogProgressIndicator( + mainFrame, + progressIndicatorTitle, + new String[]{Bundle.Case_progressIndicatorCancelButton_label()}, + Bundle.Case_progressIndicatorCancelButton_label(), + cancelButtonListener); + } else { + progressIndicator = new ModalDialogProgressIndicator( + mainFrame, + progressIndicatorTitle); + } } else { progressIndicator = new LoggingProgressIndicator(); } progressIndicator.start(Bundle.Case_progressMessage_preparing()); /* - * Creating/opening a case is always done by creating a task running in - * the same non-UI thread that will be used to close the case, so a + * A case action is always done by creating a task running in the same + * non-UI thread that will be used to close the case, so a * single-threaded executor service is created here and saved as case * state (must be volatile for cancellation to work). * - * --- If the case is a single-user case, this supports cancelling - * opening of the case by cancelling the task. + * --- If the case is a single-user case, this supports cancelling the + * case action by cancelling the task. * * --- If the case is a multi-user case, this still supports - * cancellation, but it also makes it possible for the shared case - * directory lock held as long as the case is open to be released in the - * same thread in which it was acquired, as is required by the - * coordination service. + * cancellation, but it also makes it possible for the case lock held as + * long as the case is open to be released in the same thread in which + * it was acquired, as is required by the coordination service. */ TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName())); caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory); Future future = caseLockingExecutor.submit(() -> { if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { - open(isNewCase, progressIndicator); + caseAction.execute(progressIndicator, additionalParams); } else { /* - * First, acquire a shared case directory lock that will be held - * as long as this node has this case open. This will prevent - * deletion of the case by another node. Next, acquire an - * exclusive case resources lock to ensure only one node at a - * time can create/open/upgrade/close the case resources. + * First, acquire a case lock that will be held as long as this + * node has this case open. This will prevent deletion of the + * case by another node. Next, acquire an exclusive case + * resources lock to ensure only one node at a time can + * create/open/upgrade/close the case resources. */ progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources()); - acquireSharedCaseDirLock(metadata.getCaseDirectory()); + acquireCaseLock(caseLockType, metadata.getCaseDirectory()); try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) { if (null == resourcesLock) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock()); } - open(isNewCase, progressIndicator); + caseAction.execute(progressIndicator, additionalParams); } catch (CaseActionException ex) { releaseSharedCaseDirLock(getMetadata().getCaseDirectory()); 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 { future.get(); } catch (InterruptedException discarded) { /* * 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 * completed with a cancellation condition, the case will have been - * closed and the case directory lock released will have been - * released. + * closed and the case lock will have been released. */ if (null != cancelButtonListener) { cancelButtonListener.actionPerformed(null); @@ -1908,21 +1875,20 @@ public class Case { ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); } 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 * the task is completed with a cancellation condition, the case - * will have been closed and the case directory lock released will - * have been released. + * will have been closed and the case lock will have been released. */ ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); } 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 * because if the task is completed with an execution condition, the - * case will have been closed and the case directory lock released - * will have been released. + * case will have been closed and the case lock will have been + * released. */ ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); 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) 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 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 - * exception will have a user-friendly message - * and may be a wrapper for a lower-level - * exception. + * @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 open(boolean isNewCase, ProgressIndicator progressIndicator) throws CaseActionException { + private Void createCase(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { + assert (additionalParams == null); try { checkForUserCancellation(); createCaseDirectoryIfDoesNotExist(progressIndicator); checkForUserCancellation(); switchLoggingToCaseLogsDirectory(progressIndicator); checkForUserCancellation(); - if (isNewCase) { - saveCaseMetadataToFile(progressIndicator); - } + saveCaseMetadataToFile(progressIndicator); checkForUserCancellation(); - if (isNewCase) { - createCaseNodeData(progressIndicator); - } else { - updateCaseNodeData(progressIndicator); - } + createCaseNodeData(progressIndicator); checkForUserCancellation(); - if (!isNewCase) { - deleteTempfilesFromCaseDirectory(progressIndicator); - } checkForUserCancellation(); - if (isNewCase) { - createCaseDatabase(progressIndicator); - } else { - openCaseDataBase(progressIndicator); - } + createCaseDatabase(progressIndicator); checkForUserCancellation(); openCaseLevelServices(progressIndicator); checkForUserCancellation(); openAppServiceCaseResources(progressIndicator); checkForUserCancellation(); openCommunicationChannels(progressIndicator); + return null; } catch (CaseActionException ex) { /* @@ -1991,6 +1950,96 @@ public class Case { } } + /** + * A case action (interface CaseAction) 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) 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 * @@ -2475,7 +2524,7 @@ public class Case { /* * The wait has been interrupted by interrupting the thread running * 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. */ } catch (ExecutionException ex) { @@ -2595,9 +2644,13 @@ public class Case { * cannot be acquired. */ @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 { - 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) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock()); } @@ -2994,6 +3047,15 @@ public class Case { } } + private interface CaseAction { + + R execute(T t, V v) throws CaseActionException; + } + + private enum CaseLockType { + SHARED, EXCLUSIVE; + } + /** * A case operation Cancel button listener for use with a * ModalDialogProgressIndicator when running with a GUI. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java new file mode 100644 index 0000000000..528042d63d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -0,0 +1,107 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit 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() { + + @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; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java index 789913d78a..ec959cbaf4 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.casemodule; +import org.sleuthkit.autopsy.access.AccessLimiterUtils; import java.awt.Component; import org.openide.util.NbBundle; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index bdf53880c4..ca93ba9040 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2019 Basis Technology Corp. + * Copyright 2012-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * 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.PropertyChangeListener; -import java.io.File; -import java.nio.file.Paths; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; @@ -35,14 +33,14 @@ import org.apache.commons.lang3.tuple.Pair; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; 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.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor; import org.sleuthkit.autopsy.directorytree.FileSearchAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; @@ -66,10 +64,6 @@ public class ImageNode extends AbstractContentNode { private static final Logger logger = Logger.getLogger(ImageNode.class.getName()); private static final Set 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 @@ -128,7 +122,7 @@ public class ImageNode extends AbstractContentNode { actionsList.add(new RunIngestModulesAction(Collections.singletonList(content))); actionsList.add(new NewWindowViewAction( NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this)); - if (checkSchemaVersion() && checkMuAdmin()) { + if (canAddDeleteDataSourceAction()) { actionsList.add(new DeleteDataSourceAction(content.getId())); } return actionsList.toArray(new Action[0]); @@ -218,31 +212,19 @@ public class ImageNode extends AbstractContentNode { return getClass().getName(); } - private Boolean checkSchemaVersion() { - try { - CaseDbSchemaVersionNumber creationVersion = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaCreationVersion(); - - if ((creationVersion.getMajor() == 8 && creationVersion.getMinor() >= 3) || creationVersion.getMajor() > 8) { - return true; - } - } catch (NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Failed to get creation schema version: ", ex); - } - - 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); + /** + * Determines whether or not the delete data source action can be added. + * @return True or false. + */ + private Boolean canAddDeleteDataSourceAction() { + boolean canAddAction = false; + CaseDbSchemaVersionNumber creationVersion = Case.getCurrentCase().getSleuthkitCase().getDBSchemaCreationVersion(); + if ((creationVersion.getMajor() == 8 && creationVersion.getMinor() >= 3) || (creationVersion.getMajor() > 8)) { + canAddAction = Case.getCurrentCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE || !AccessLimiterUtils.limitMultiUserAccess(); } - return true; + return canAddAction; } - + /* * This property change listener refreshes the tree when a new file is * carved out of this image (i.e, the image is being treated as raw bytes From 6c8e74fc42793ab7af0d8cd7c4af7b78eb29d8e2 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 30 Oct 2019 16:55:53 -0400 Subject: [PATCH 09/37] Integrate and update data src deletion --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 2 +- .../sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java | 2 ++ .../org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java | 2 +- Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java | 4 ++-- .../autopsy/{access => featureaccess}/AccessLimiterUtils.java | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) rename Core/src/org/sleuthkit/autopsy/{access => featureaccess}/AccessLimiterUtils.java (97%) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 715a4383b7..21916cda6e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -18,7 +18,7 @@ */ package org.sleuthkit.autopsy.casemodule; -import org.sleuthkit.autopsy.access.AccessLimiterUtils; +import org.sleuthkit.autopsy.featureaccess.AccessLimiterUtils; import com.google.common.annotations.Beta; import com.google.common.eventbus.Subscribe; import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index 528042d63d..2eb9d4930e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -27,6 +27,7 @@ import javax.swing.SwingWorker; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.ingest.IngestManager; /** * An Action that allows a user to delete a data source from the current case. @@ -49,6 +50,7 @@ public final class DeleteDataSourceAction extends AbstractAction { public DeleteDataSourceAction(Long dataSourceObjectID) { super(Bundle.DeleteDataSourceAction_name_text()); this.dataSourceObjectID = dataSourceObjectID; + this.setEnabled(!IngestManager.getInstance().isIngestRunning()); } @NbBundle.Messages({ diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java index ec959cbaf4..cbe1a87847 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java @@ -18,7 +18,7 @@ */ package org.sleuthkit.autopsy.casemodule; -import org.sleuthkit.autopsy.access.AccessLimiterUtils; +import org.sleuthkit.autopsy.featureaccess.AccessLimiterUtils; import java.awt.Component; import org.openide.util.NbBundle; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index ca93ba9040..f1aab51355 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -33,7 +33,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.access.AccessLimiterUtils; +import org.sleuthkit.autopsy.featureaccess.AccessLimiterUtils; import org.sleuthkit.autopsy.casemodule.DeleteDataSourceAction; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; @@ -123,7 +123,7 @@ public class ImageNode extends AbstractContentNode { actionsList.add(new NewWindowViewAction( NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this)); if (canAddDeleteDataSourceAction()) { - actionsList.add(new DeleteDataSourceAction(content.getId())); + actionsList.add(new DeleteDataSourceAction(content.getId())); } return actionsList.toArray(new Action[0]); } diff --git a/Core/src/org/sleuthkit/autopsy/access/AccessLimiterUtils.java b/Core/src/org/sleuthkit/autopsy/featureaccess/AccessLimiterUtils.java similarity index 97% rename from Core/src/org/sleuthkit/autopsy/access/AccessLimiterUtils.java rename to Core/src/org/sleuthkit/autopsy/featureaccess/AccessLimiterUtils.java index 31f3a36bc8..5f352939b5 100644 --- a/Core/src/org/sleuthkit/autopsy/access/AccessLimiterUtils.java +++ b/Core/src/org/sleuthkit/autopsy/featureaccess/AccessLimiterUtils.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.access; +package org.sleuthkit.autopsy.featureaccess; import java.io.File; import java.nio.file.Paths; From 7c72d08c8c0037c70dfc01cf3f3945845c9051d5 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 30 Oct 2019 17:21:11 -0400 Subject: [PATCH 10/37] Integrate and update data src deletion --- .../casemodule/Bundle.properties-MERGED | 28 +++++++------------ .../sleuthkit/autopsy/casemodule/Case.java | 11 ++++++-- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 824ee91474..a3adf9e171 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -9,7 +9,6 @@ Case.deleteCaseConfirmationDialog.title=Delete Current Case? # {0} - exception message Case.deleteCaseFailureMessageBox.message=Error deleting case: {0} Case.deleteCaseFailureMessageBox.title=Failed to Delete Case -Case.DeletingDataSourceFromCase=Deleting the Data Source from the case. Case.exceptionMessage.cancelledByUser=Cancelled by user. 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. @@ -33,6 +32,7 @@ Case.exceptionMessage.couldNotSaveCaseMetadata=Failed to save case metadata:\n{0 Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}. # {0} - exception message Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination service node data:\n{0}. +Case.exceptionMessage.dataSourceNotFound=The data source was not found. # {0} - case display name Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled. Case.exceptionMessage.emptyCaseDir=Must specify a case directory path. @@ -71,6 +71,7 @@ Case.progressMessage.creatingCaseNodeData=Creating coordination service node dat Case.progressMessage.deletingCaseDatabase=Deleting case database... Case.progressMessage.deletingCaseDirCoordSvcNode=Deleting case directory coordination service node... Case.progressMessage.deletingCaseDirectory=Deleting case directory... +Case.progressMessage.deletingDataSource=Deleting the data source from the case... Case.progressMessage.deletingResourcesCoordSvcNode=Deleting case resources coordination service node... Case.progressMessage.deletingTextIndex=Deleting text index... Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case... @@ -238,15 +239,10 @@ AddImageWizardIngestConfigPanel.dsProcDone.errs.text=*Errors encountered in addi AddImageWizardIngestConfigVisual.getName.text=Configure Ingest Modules AddImageWizardIterator.stepXofN=Step {0} of {1} AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1} -Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open\! +Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open! Case.open.msgDlg.updated.msg=Updated case database schema.\nA backup copy of the database with the following path has been made:\n {0} Case.open.msgDlg.updated.title=Case Database Schema Update -Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \n\ -this case are missing. Would you like to search for them now?\n\ -Previously, the image was located at:\n\ -{0}\n\ -Please note that you will still be able to browse directories and generate reports\n\ -if you choose No, but you will not be able to view file content or run the ingest process. +Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \nthis case are missing. Would you like to search for them now?\nPreviously, the image was located at:\n{0}\nPlease note that you will still be able to browse directories and generate reports\nif you choose No, but you will not be able to view file content or run the ingest process. Case.checkImgExist.confDlg.doesntExist.title=Missing Image Case.addImg.exception.msg=Error adding image to the case Case.updateCaseName.exception.msg=Error while trying to update the case name. @@ -265,12 +261,9 @@ Case.GetCaseTypeGivenPath.Failure=Unable to get case type Case.metaDataFileCorrupt.exception.msg=The case metadata file (.aut) is corrupted. Case.deleteReports.deleteFromDiskException.log.msg=Unable to delete the report from the disk. Case.deleteReports.deleteFromDiskException.msg=Unable to delete the report {0} from the disk.\nYou may manually delete it from {1} -CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \n\ - Case Name: {0}\n\ - Case Directory: {1} +CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \nCase Name: {0}\nCase Directory: {1} CaseDeleteAction.closeConfMsg.title=Warning: Closing the Current Case -CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\n\ -Close the folder and file and try again or you can delete the case manually. +CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\nClose the folder and file and try again or you can delete the case manually. CaseDeleteAction.msgDlg.fileInUse.title=Error: Folder In Use CaseDeleteAction.msgDlg.caseDelete.msg=Case {0} has been deleted. CaseOpenAction.autFilter.title={0} Case File ( {1}) @@ -302,8 +295,7 @@ NewCaseWizardAction.databaseProblem1.text=Cannot open database. Cancelling case NewCaseWizardAction.databaseProblem2.text=Error NewCaseWizardPanel1.validate.errMsg.invalidSymbols=The Case Name cannot contain any of the following symbols: \\ / : * ? " < > | NewCaseWizardPanel1.validate.errMsg.dirExists=Case directory ''{0}'' already exists. -NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\n\ - Do you want to create that directory? +NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\nDo you want to create that directory? NewCaseWizardPanel1.validate.confMsg.createDir.title=Create directory NewCaseWizardPanel1.validate.errMsg.cantCreateParDir.msg=Error: Could not create case parent directory {0} NewCaseWizardPanel1.validate.errMsg.prevCreateBaseDir.msg=Prevented from creating base directory {0} @@ -353,15 +345,15 @@ UnpackageWorker.doInBackground.errorFinding7zip=Could not locate 7-Zip executabl UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases UpdateRecentCases.menuItem.empty=-Empty- AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel -NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive -NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on \"C:\" drive. Case folder is created on the target system +NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on "C:" drive +NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on "C:" drive. Case folder is created on the target system NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning: Path to case folder is on the target system. Create case folder in mounted drive. CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1} MissingImageDialog.lbWarning.text= MissingImageDialog.lbWarning.toolTipText= NewCaseVisualPanel1.caseParentDirWarningLabel.text= -NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user +NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user\t\t NewCaseVisualPanel1.singleUserCaseRadioButton.text=Single-user NewCaseVisualPanel1.caseTypeLabel.text=Case Type: SingleUserCaseConverter.BadDatabaseFileName=Database file does not exist! diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 21916cda6e..c52ac770ec 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -124,12 +124,14 @@ import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.CaseDbConnectionInfo; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Report; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TimelineManager; import org.sleuthkit.datamodel.SleuthkitCaseAdmin; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskDataException; import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException; /** @@ -2018,7 +2020,8 @@ public class Case { * lower-level exception. */ @Messages({ - "Case.DeletingDataSourceFromCase=Deleting the Data Source from the case.", + "Case.progressMessage.deletingDataSource=Deleting the data source from the case...", + "Case.exceptionMessage.dataSourceNotFound=The data source was not found.", "# {0} - exception message", "Case.exceptionMessage.errorDeletingDataSource=An error occurred while deleting the data source:\n{0}." }) Void deleteDataSource(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { @@ -2027,8 +2030,12 @@ public class Case { openAppServiceCaseResources(progressIndicator); Long dataSourceObjectID = (Long) additionalParams; try { + DataSource dataSource = this.caseDb.getDataSource(dataSourceObjectID); + if (dataSource == null) { + throw new CaseActionException(Bundle.Case_exceptionMessage_dataSourceNotFound()); + } SleuthkitCaseAdmin.deleteDataSource(this.caseDb, dataSourceObjectID); - } catch (TskCoreException ex) { + } catch (TskDataException | TskCoreException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSource(ex.getMessage()), ex); } try { From b9868674c181c4b98c3a963703a212d145b1856f Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 30 Oct 2019 18:00:43 -0400 Subject: [PATCH 11/37] Integrate and update data src deletion --- .../casemodule/Bundle.properties-MERGED | 25 ++++--- .../sleuthkit/autopsy/casemodule/Case.java | 66 +++++++++++-------- .../casemodule/DeleteDataSourceAction.java | 13 +++- .../autopsy/datamodel/ImageNode.java | 7 +- .../imagegallery/datamodel/DrawableDB.java | 2 - 5 files changed, 69 insertions(+), 44 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index a3adf9e171..210b720b36 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -239,10 +239,15 @@ AddImageWizardIngestConfigPanel.dsProcDone.errs.text=*Errors encountered in addi AddImageWizardIngestConfigVisual.getName.text=Configure Ingest Modules AddImageWizardIterator.stepXofN=Step {0} of {1} AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1} -Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open! +Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open\! Case.open.msgDlg.updated.msg=Updated case database schema.\nA backup copy of the database with the following path has been made:\n {0} Case.open.msgDlg.updated.title=Case Database Schema Update -Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \nthis case are missing. Would you like to search for them now?\nPreviously, the image was located at:\n{0}\nPlease note that you will still be able to browse directories and generate reports\nif you choose No, but you will not be able to view file content or run the ingest process. +Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \n\ +this case are missing. Would you like to search for them now?\n\ +Previously, the image was located at:\n\ +{0}\n\ +Please note that you will still be able to browse directories and generate reports\n\ +if you choose No, but you will not be able to view file content or run the ingest process. Case.checkImgExist.confDlg.doesntExist.title=Missing Image Case.addImg.exception.msg=Error adding image to the case Case.updateCaseName.exception.msg=Error while trying to update the case name. @@ -261,9 +266,12 @@ Case.GetCaseTypeGivenPath.Failure=Unable to get case type Case.metaDataFileCorrupt.exception.msg=The case metadata file (.aut) is corrupted. Case.deleteReports.deleteFromDiskException.log.msg=Unable to delete the report from the disk. Case.deleteReports.deleteFromDiskException.msg=Unable to delete the report {0} from the disk.\nYou may manually delete it from {1} -CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \nCase Name: {0}\nCase Directory: {1} +CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \n\ + Case Name: {0}\n\ + Case Directory: {1} CaseDeleteAction.closeConfMsg.title=Warning: Closing the Current Case -CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\nClose the folder and file and try again or you can delete the case manually. +CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\n\ +Close the folder and file and try again or you can delete the case manually. CaseDeleteAction.msgDlg.fileInUse.title=Error: Folder In Use CaseDeleteAction.msgDlg.caseDelete.msg=Case {0} has been deleted. CaseOpenAction.autFilter.title={0} Case File ( {1}) @@ -295,7 +303,8 @@ NewCaseWizardAction.databaseProblem1.text=Cannot open database. Cancelling case NewCaseWizardAction.databaseProblem2.text=Error NewCaseWizardPanel1.validate.errMsg.invalidSymbols=The Case Name cannot contain any of the following symbols: \\ / : * ? " < > | NewCaseWizardPanel1.validate.errMsg.dirExists=Case directory ''{0}'' already exists. -NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\nDo you want to create that directory? +NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\n\ + Do you want to create that directory? NewCaseWizardPanel1.validate.confMsg.createDir.title=Create directory NewCaseWizardPanel1.validate.errMsg.cantCreateParDir.msg=Error: Could not create case parent directory {0} NewCaseWizardPanel1.validate.errMsg.prevCreateBaseDir.msg=Prevented from creating base directory {0} @@ -345,15 +354,15 @@ UnpackageWorker.doInBackground.errorFinding7zip=Could not locate 7-Zip executabl UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases UpdateRecentCases.menuItem.empty=-Empty- AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel -NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on "C:" drive -NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on "C:" drive. Case folder is created on the target system +NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive +NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on \"C:\" drive. Case folder is created on the target system NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning: Path to case folder is on the target system. Create case folder in mounted drive. CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1} MissingImageDialog.lbWarning.text= MissingImageDialog.lbWarning.toolTipText= NewCaseVisualPanel1.caseParentDirWarningLabel.text= -NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user\t\t +NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user NewCaseVisualPanel1.singleUserCaseRadioButton.text=Single-user NewCaseVisualPanel1.caseTypeLabel.text=Case Type: SingleUserCaseConverter.BadDatabaseFileName=Database file does not exist! diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index c52ac770ec..9677f35b6a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -53,7 +53,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; -import java.util.function.Function; import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -745,7 +744,7 @@ public class Case { } /** - * Deletes a data source from the current cases. + * Deletes a data source from the current case. * * @param dataSourceObjectID The object ID of the data source to delete. * @@ -764,7 +763,10 @@ public class Case { } Case theCase = currentCase; closeCurrentCase(); - theCase.doCaseAction(Bundle.Case_progressIndicatorTitle_deletingDataSource(), null, CaseLockType.EXCLUSIVE, false, dataSourceObjectID); + /* + * Note that this case action does not support cancellation. + */ + theCase.doCaseAction(Bundle.Case_progressIndicatorTitle_deletingDataSource(), theCase::deleteDataSource, CaseLockType.EXCLUSIVE, false, dataSourceObjectID); openAsCurrentCase(theCase, false); } } @@ -1754,12 +1756,7 @@ public class Case { } /** - * Performs a case action by creating a task running in the same non-UI - * thread that will be used to close the case. For both single-user and - * mulit-user cases, this supports cancelling the case action by cancelling - * the task. If the case is a multi-user case, this also ensures that the - * case lock is released in the same thread in which it was acquired, which - * is required by the coordination service. + * Performs a case action. * * @param progressIndicatorTitle A title for the progress indicator for the * case action. @@ -1769,8 +1766,7 @@ public class Case { * @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. + * parameters for a case action. * * @throws CaseActionException If there is a problem completing the action. * The exception will have a user-friendly @@ -1815,15 +1811,11 @@ public class Case { * A case action is always done by creating a task running in the same * non-UI thread that will be used to close the case, so a * single-threaded executor service is created here and saved as case - * state (must be volatile for cancellation to work). - * - * --- If the case is a single-user case, this supports cancelling the - * case action by cancelling the task. - * - * --- If the case is a multi-user case, this still supports - * cancellation, but it also makes it possible for the case lock held as - * long as the case is open to be released in the same thread in which - * it was acquired, as is required by the coordination service. + * state (must be volatile for cancellation to work). For both + * single-user and mulit-user cases, this supports cancelling the case + * action by cancelling the task. If the case is a multi-user case, this + * also ensures that the case lock is released in the same thread in + * which it was acquired, which is required by the coordination service. */ TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName())); caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory); @@ -1954,7 +1946,7 @@ public class Case { /** * A case action (interface CaseAction) that opens the case - * database and services for this case. + * database and application services for this case. * * @param progressIndicator A progress indicator. * @param additionalParams An Object that holds any additional parameters @@ -2007,8 +1999,6 @@ public class Case { * 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 @@ -2643,17 +2633,15 @@ public class Case { } /** - * Acquires a shared case directory lock for the current case. + * Acquires a case (case directory) lock for the current case. * * @param caseDir The full path of the case directory. * - * @throws CaseActionException with a user-friendly message if the lock - * cannot be acquired. + * @throws CaseActionException If the lock cannot be acquired. */ @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory"}) private void acquireCaseLock(CaseLockType lockType, String caseDir) throws CaseActionException { try { - boolean flag = true; CoordinationService coordinationService = CoordinationService.getInstance(); caseDirLock = lockType == CaseLockType.SHARED ? coordinationService.tryGetSharedLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS) @@ -3054,11 +3042,33 @@ public class Case { } } + /** + * Defines the signature for case action methods that can be passed as + * arguments to the doCaseAction method. + * + * @param A ProgressIndicator + * @param The optional parameters stored in an Object. + * @param The return type of Void. + */ private interface CaseAction { - R execute(T t, V v) throws CaseActionException; + /** + * The signature for a case action method. + * + * @param progressIndicator A ProgressIndicator. + * @param additionalParams The optional parameters stored in an Object. + * + * @return A Void object (null). + * + * @throws CaseActionException + */ + R execute(T progressIndicator, V additionalParams) throws CaseActionException; } + /** + * The choices for the case (case directory) coordination service lock used + * for multi-user cases. + */ private enum CaseLockType { SHARED, EXCLUSIVE; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index 2eb9d4930e..e441428851 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -56,7 +56,8 @@ public final class DeleteDataSourceAction extends AbstractAction { @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.",}) + "DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to reopen the case." + }) @Override public void actionPerformed(ActionEvent event) { if (MessageNotifyUtil.Message.confirm(Bundle.DeleteDataSourceAction_confirmationDialog_message())) { @@ -102,8 +103,14 @@ public final class DeleteDataSourceAction extends AbstractAction { return clonedObject; } - private void setDataSourceID(long dataSourceID) { - this.dataSourceObjectID = dataSourceID; + /** + * Allows the setting of the data source object ID field of a clone of this + * action. + * + * @param dataSourceObjectID The data source object ID. + */ + private void setDataSourceID(long dataSourceObjectID) { + this.dataSourceObjectID = dataSourceObjectID; } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index f1aab51355..0f83f7fe4c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -123,7 +123,7 @@ public class ImageNode extends AbstractContentNode { actionsList.add(new NewWindowViewAction( NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this)); if (canAddDeleteDataSourceAction()) { - actionsList.add(new DeleteDataSourceAction(content.getId())); + actionsList.add(new DeleteDataSourceAction(content.getId())); } return actionsList.toArray(new Action[0]); } @@ -213,14 +213,15 @@ public class ImageNode extends AbstractContentNode { } /** - * Determines whether or not the delete data source action can be added. + * Determines whether or not the delete data source action can be added. + * * @return True or false. */ private Boolean canAddDeleteDataSourceAction() { boolean canAddAction = false; CaseDbSchemaVersionNumber creationVersion = Case.getCurrentCase().getSleuthkitCase().getDBSchemaCreationVersion(); if ((creationVersion.getMajor() == 8 && creationVersion.getMinor() >= 3) || (creationVersion.getMajor() > 8)) { - canAddAction = Case.getCurrentCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE || !AccessLimiterUtils.limitMultiUserAccess(); + canAddAction = Case.getCurrentCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE || !AccessLimiterUtils.limitMultiUserAccess(); } return canAddAction; } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java index 83d8d8df19..d875657cc1 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java @@ -56,10 +56,8 @@ import javax.swing.SortOrder; import static org.apache.commons.lang3.ObjectUtils.notEqual; import org.apache.commons.lang3.StringUtils; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.DhsImageCategory; -import org.sleuthkit.autopsy.events.AutopsyEventPublisher; import org.sleuthkit.autopsy.imagegallery.FileTypeUtils; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; import org.sleuthkit.autopsy.imagegallery.ImageGalleryModule; From d56be42c9f30233f431378b1868c6b79cd4baa1b Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 1 Nov 2019 12:44:00 -0400 Subject: [PATCH 12/37] Update data src deletion feature --- .../autopsy/appservices/AutopsyService.java | 12 +++-- .../casemodule/Bundle.properties-MERGED | 2 +- .../sleuthkit/autopsy/casemodule/Case.java | 54 ++++++++++--------- .../casemodule/DeleteDataSourceAction.java | 2 +- .../autopsy/coreutils/ThreadUtils.java | 2 +- .../keywordsearch/SolrSearchService.java | 2 +- 6 files changed, 41 insertions(+), 33 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/appservices/AutopsyService.java b/Core/src/org/sleuthkit/autopsy/appservices/AutopsyService.java index 7ef7e30f01..1ad16e999d 100644 --- a/Core/src/org/sleuthkit/autopsy/appservices/AutopsyService.java +++ b/Core/src/org/sleuthkit/autopsy/appservices/AutopsyService.java @@ -50,7 +50,9 @@ public interface AutopsyService { * @param context The case context which includes things such as the case, a * progress indicator for the operation, a cancellation * request flag, etc. - * @throws org.sleuthkit.autopsy.framework.AutopsyService.AutopsyServiceException + * + * @throws + * org.sleuthkit.autopsy.framework.AutopsyService.AutopsyServiceException */ default void openCaseResources(CaseContext context) throws AutopsyServiceException { /* @@ -64,7 +66,9 @@ public interface AutopsyService { * @param context The case context which includes things such as the case, a * progress indicator for the operation, a cancellation * request flag, etc. - * @throws org.sleuthkit.autopsy.framework.AutopsyService.AutopsyServiceException + * + * @throws + * org.sleuthkit.autopsy.framework.AutopsyService.AutopsyServiceException */ default void closeCaseResources(CaseContext context) throws AutopsyServiceException { /* @@ -113,7 +117,9 @@ public interface AutopsyService { /** * Gets the progress indicator for the creation/opening/upgrading of - * case-level resources by a service. + * case-level resources by a service. IMPORTANT: The service should only + * call progress() on the progress indicator. Calling start() and + * finsih() are the responsibility opf the case. * * @return The progress indicator. */ diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 210b720b36..eb346144f9 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -123,7 +123,7 @@ CTL_CaseDetailsAction=Case Details CTL_CaseDeleteAction=Delete Case CTL_CaseOpenAction=Open 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.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?\n Note that the case will be closed and re-opened several times to complete the deletion. 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 diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 9677f35b6a..81f9bf0dc2 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -164,7 +164,7 @@ public class Case { private CollaborationMonitor collaborationMonitor; private Services caseServices; private boolean hasDataSources; - private final TSKCaseRepublisher tskEventForwarder = new TSKCaseRepublisher(); + private final TSKCaseRepublisher tskEventForwarder; /* * Get a reference to the main window of the desktop application to use to @@ -172,11 +172,8 @@ public class Case { * changing the main window title. */ static { - WindowManager.getDefault().invokeWhenUIReady(new Runnable() { - @Override - public void run() { + WindowManager.getDefault().invokeWhenUIReady(() -> { mainFrame = WindowManager.getDefault().getMainWindow(); - } }); } @@ -673,14 +670,15 @@ public class Case { * method should put all operations in an exception firewall with a try and * catch-all block to handle the possibility of bad timing. * - * TODO (JIRA-3825): Introduce a reference counting scheme for this get case - * method. - * * @return The current case. * * @throws NoCurrentCaseException if there is no current case. */ public static Case getCurrentCaseThrows() throws NoCurrentCaseException { + /* + * TODO (JIRA-3825): Introduce a reference counting scheme for this get + * case method. + */ Case openCase = currentCase; if (openCase == null) { throw new NoCurrentCaseException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen")); @@ -761,13 +759,11 @@ public class Case { if (null == currentCase) { return; } - Case theCase = currentCase; + CaseMetadata caseMetadata = currentCase.getMetadata(); closeCurrentCase(); - /* - * Note that this case action does not support cancellation. - */ + Case theCase = new Case(caseMetadata); theCase.doCaseAction(Bundle.Case_progressIndicatorTitle_deletingDataSource(), theCase::deleteDataSource, CaseLockType.EXCLUSIVE, false, dataSourceObjectID); - openAsCurrentCase(theCase, false); + openAsCurrentCase(new Case(caseMetadata), false); } } @@ -1743,7 +1739,7 @@ public class Case { * */ private Case(CaseType caseType, String caseDir, CaseDetails caseDetails) { - metadata = new CaseMetadata(caseType, caseDir, displayNameToUniqueName(caseDetails.getCaseDisplayName()), caseDetails); + this(new CaseMetadata(caseType, caseDir, displayNameToUniqueName(caseDetails.getCaseDisplayName()), caseDetails)); } /** @@ -1753,6 +1749,9 @@ public class Case { */ private Case(CaseMetadata caseMetaData) { metadata = caseMetaData; + TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName())); + caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory); + tskEventForwarder = new TSKCaseRepublisher(); } /** @@ -1817,8 +1816,6 @@ public class Case { * also ensures that the case lock is released in the same thread in * which it was acquired, which is required by the coordination service. */ - TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName())); - caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory); Future future = caseLockingExecutor.submit(() -> { if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { caseAction.execute(progressIndicator, additionalParams); @@ -1866,7 +1863,7 @@ public class Case { } else { future.cancel(true); } - ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); + ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); // RJCTODO } catch (CancellationException discarded) { /* * The case action task has been cancelled. Wait for it to finish, @@ -1874,7 +1871,7 @@ public class Case { * the task is completed with a cancellation condition, the case * will have been closed and the case lock will have been released. */ - ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); + ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); // RJCTODO throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); } catch (ExecutionException ex) { /* @@ -1884,7 +1881,7 @@ public class Case { * case will have been closed and the case lock will have been * released. */ - ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); + ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); // RJCTODO throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex); } finally { progressIndicator.finish(); @@ -2016,8 +2013,8 @@ public class Case { }) Void deleteDataSource(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { assert (additionalParams instanceof Long); - openCaseDataBase(progressIndicator); - openAppServiceCaseResources(progressIndicator); + openCase(progressIndicator, null); + progressIndicator.progress(Bundle.Case_progressMessage_deletingDataSource()); Long dataSourceObjectID = (Long) additionalParams; try { DataSource dataSource = this.caseDb.getDataSource(dataSourceObjectID); @@ -2034,6 +2031,7 @@ public class Case { throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSource(ex.getMessage()), ex); } eventPublisher.publish(new DataSourceDeletedEvent(dataSourceObjectID)); + close(progressIndicator); return null; } @@ -2092,9 +2090,6 @@ public class Case { /** * Creates the case directory, if it does not already exist. * - * TODO (JIRA-2180): Always create the case directory as part of the case - * creation process. - * * @param progressIndicator A progress indicator. * * @throws CaseActionException If there is a problem completing the @@ -2106,6 +2101,10 @@ public class Case { "Case.progressMessage.creatingCaseDirectory=Creating case directory..." }) private void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator) throws CaseActionException { + /* + * TODO (JIRA-2180): Always create the case directory as part of the + * case creation process. + */ progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory()); if (new File(metadata.getCaseDirectory()).exists() == false) { progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory()); @@ -2315,7 +2314,6 @@ public class Case { private void openCaseLevelServices(ProgressIndicator progressIndicator) { progressIndicator.progress(Bundle.Case_progressMessage_openingCaseLevelServices()); this.caseServices = new Services(caseDb); - caseDb.registerForEvents(tskEventForwarder); } @@ -2527,7 +2525,6 @@ public class Case { } catch (ExecutionException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex); } finally { - ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); progressIndicator.finish(); } } @@ -2573,6 +2570,11 @@ public class Case { caseDb.close(); } + /* + * Release the case (case directory) lock. + */ + releaseSharedCaseDirLock(getMetadata().getCaseDirectory()); + /* * Switch the log directory. */ diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index e441428851..0135c4ef18 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -54,7 +54,7 @@ public final class DeleteDataSourceAction extends AbstractAction { } @NbBundle.Messages({ - "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?", + "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?\n Note that the case will be closed and re-opened several times to complete the deletion.", "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." }) diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ThreadUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/ThreadUtils.java index b9873469e7..0671290407 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ThreadUtils.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ThreadUtils.java @@ -44,7 +44,7 @@ final public class ThreadUtils { } catch (InterruptedException ignored) { /* * Ignore interrupts. The policy implemented by this method is - * an unconditional wait.: + * an unconditional wait. */ } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java index e2906f89d6..94dd567c5e 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java @@ -294,7 +294,7 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService { String caseDirPath = context.getCase().getCaseDirectory(); Case theCase = context.getCase(); List indexes = new ArrayList<>(); - progress.start(Bundle.SolrSearch_lookingForMetadata_msg(), totalNumProgressUnits); + progress.progress(Bundle.SolrSearch_lookingForMetadata_msg(), totalNumProgressUnits); if (IndexMetadata.isMetadataFilePresent(caseDirPath)) { try { // metadata file exists, get list of existing Solr cores for this case From 35277504f97df7457ea5a490b05706c59986c798 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 1 Nov 2019 13:06:45 -0400 Subject: [PATCH 13/37] Integrate and update data src deletion --- Core/src/org/sleuthkit/autopsy/appservices/AutopsyService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/appservices/AutopsyService.java b/Core/src/org/sleuthkit/autopsy/appservices/AutopsyService.java index 1ad16e999d..3e67e6a442 100644 --- a/Core/src/org/sleuthkit/autopsy/appservices/AutopsyService.java +++ b/Core/src/org/sleuthkit/autopsy/appservices/AutopsyService.java @@ -119,7 +119,7 @@ public interface AutopsyService { * Gets the progress indicator for the creation/opening/upgrading of * case-level resources by a service. IMPORTANT: The service should only * call progress() on the progress indicator. Calling start() and - * finsih() are the responsibility opf the case. + * finish() are the responsibility of the case providing the context. * * @return The progress indicator. */ From c47dd70adea967383b63ad8648eb1f118003a627 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 6 Nov 2019 16:22:41 -0500 Subject: [PATCH 14/37] Data source deletion --- .../casemodule/Bundle.properties-MERGED | 2 +- .../sleuthkit/autopsy/casemodule/Case.java | 50 +++++++++---------- .../casemodule/DeleteDataSourceAction.java | 2 +- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index eb346144f9..9286a181cd 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -124,7 +124,7 @@ CTL_CaseDeleteAction=Delete Case CTL_CaseOpenAction=Open 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?\n Note that the case will be closed and re-opened several times to complete the deletion. -DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to reopen the case. +DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open 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 diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 81f9bf0dc2..88ec63f8ea 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -173,7 +173,7 @@ public class Case { */ static { WindowManager.getDefault().invokeWhenUIReady(() -> { - mainFrame = WindowManager.getDefault().getMainWindow(); + mainFrame = WindowManager.getDefault().getMainWindow(); }); } @@ -848,15 +848,15 @@ public class Case { try { logger.log(Level.INFO, "Opening {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS String progressIndicatorTitle; - CaseAction caseAction; + CaseAction openCaseAction; if (isNewCase) { progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_creatingCase(); - caseAction = newCurrentCase::createCase; + openCaseAction = newCurrentCase::createCase; } else { progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_openingCase(); - caseAction = newCurrentCase::openCase; + openCaseAction = newCurrentCase::openCase; } - newCurrentCase.doCaseAction(progressIndicatorTitle, caseAction, CaseLockType.SHARED, true, null); + newCurrentCase.doCaseAction(progressIndicatorTitle, openCaseAction, CaseLockType.SHARED, true, null); 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 if (RuntimeProperties.runningWithGUI()) { @@ -2014,23 +2014,28 @@ public class Case { Void deleteDataSource(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { assert (additionalParams instanceof Long); openCase(progressIndicator, null); - progressIndicator.progress(Bundle.Case_progressMessage_deletingDataSource()); - Long dataSourceObjectID = (Long) additionalParams; try { - DataSource dataSource = this.caseDb.getDataSource(dataSourceObjectID); - if (dataSource == null) { - throw new CaseActionException(Bundle.Case_exceptionMessage_dataSourceNotFound()); + progressIndicator.progress(Bundle.Case_progressMessage_deletingDataSource()); + Long dataSourceObjectID = (Long) additionalParams; + try { + DataSource dataSource = this.caseDb.getDataSource(dataSourceObjectID); + if (dataSource == null) { + throw new CaseActionException(Bundle.Case_exceptionMessage_dataSourceNotFound()); + } + SleuthkitCaseAdmin.deleteDataSource(this.caseDb, dataSourceObjectID); + } catch (TskDataException | TskCoreException ex) { + throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSource(ex.getMessage()), ex); } - SleuthkitCaseAdmin.deleteDataSource(this.caseDb, dataSourceObjectID); - } catch (TskDataException | 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)); + } catch (CaseActionException ex) { + close(progressIndicator); + throw 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)); close(progressIndicator); return null; } @@ -2507,7 +2512,7 @@ public class Case { * Always release the case directory lock that was acquired * when the case was opened. */ - releaseSharedCaseDirLock(metadata.getCaseName()); + releaseSharedCaseDirLock(metadata.getCaseDirectory()); // RJCTODO: Rename } } return null; @@ -2570,11 +2575,6 @@ public class Case { caseDb.close(); } - /* - * Release the case (case directory) lock. - */ - releaseSharedCaseDirLock(getMetadata().getCaseDirectory()); - /* * Switch the log directory. */ diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index 0135c4ef18..25339f2d17 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -56,7 +56,7 @@ public final class DeleteDataSourceAction extends AbstractAction { @NbBundle.Messages({ "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?\n Note that the case will be closed and re-opened several times to complete the deletion.", "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." + "DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case." }) @Override public void actionPerformed(ActionEvent event) { From a52786d3fca2b7561adf47e1b37aa4558a30c5fd Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 6 Nov 2019 17:38:09 -0500 Subject: [PATCH 15/37] Data source deletion support changes --- .../sleuthkit/autopsy/casemodule/Case.java | 4 ++-- .../casemodule/NewCaseVisualPanel1.java | 4 ++-- .../autopsy/datamodel/ImageNode.java | 6 +++--- ...Utils.java => UserFeatureAccessUtils.java} | 19 ++++++++++--------- 4 files changed, 17 insertions(+), 16 deletions(-) rename Core/src/org/sleuthkit/autopsy/featureaccess/{AccessLimiterUtils.java => UserFeatureAccessUtils.java} (68%) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 88ec63f8ea..16fe5ad187 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -18,7 +18,7 @@ */ package org.sleuthkit.autopsy.casemodule; -import org.sleuthkit.autopsy.featureaccess.AccessLimiterUtils; +import org.sleuthkit.autopsy.featureaccess.UserFeatureAccessUtils; import com.google.common.annotations.Beta; import com.google.common.eventbus.Subscribe; import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData; @@ -1089,7 +1089,7 @@ public class Case { /* * Enable the case-specific actions. */ - CallableSystemAction.get(AddImageAction.class).setEnabled(Case.getCurrentCase().getMetadata().getCaseType() == CaseType.SINGLE_USER_CASE || !AccessLimiterUtils.limitMultiUserAccess()); + CallableSystemAction.get(AddImageAction.class).setEnabled(Case.getCurrentCase().getMetadata().getCaseType() == CaseType.SINGLE_USER_CASE || UserFeatureAccessUtils.canCreateOrModifyMultiUserCases()); CallableSystemAction.get(CaseCloseAction.class).setEnabled(true); CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true); CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java index cbe1a87847..76492893b2 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java @@ -18,7 +18,7 @@ */ package org.sleuthkit.autopsy.casemodule; -import org.sleuthkit.autopsy.featureaccess.AccessLimiterUtils; +import org.sleuthkit.autopsy.featureaccess.UserFeatureAccessUtils; import java.awt.Component; import org.openide.util.NbBundle; @@ -62,7 +62,7 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener { */ void readSettings() { caseNameTextField.setText(""); - if (UserPreferences.getIsMultiUserModeEnabled() && !AccessLimiterUtils.limitMultiUserAccess()) { + if (UserPreferences.getIsMultiUserModeEnabled() && UserFeatureAccessUtils.canCreateOrModifyMultiUserCases()) { multiUserCaseRadioButton.setEnabled(true); multiUserCaseRadioButton.setSelected(true); } else { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 0f83f7fe4c..3827eaf08a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -33,7 +33,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.featureaccess.AccessLimiterUtils; +import org.sleuthkit.autopsy.featureaccess.UserFeatureAccessUtils; import org.sleuthkit.autopsy.casemodule.DeleteDataSourceAction; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; @@ -220,8 +220,8 @@ public class ImageNode extends AbstractContentNode { private Boolean canAddDeleteDataSourceAction() { boolean canAddAction = false; CaseDbSchemaVersionNumber creationVersion = Case.getCurrentCase().getSleuthkitCase().getDBSchemaCreationVersion(); - if ((creationVersion.getMajor() == 8 && creationVersion.getMinor() >= 3) || (creationVersion.getMajor() > 8)) { - canAddAction = Case.getCurrentCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE || !AccessLimiterUtils.limitMultiUserAccess(); + if ((creationVersion.getMajor() == 8 && creationVersion.getMinor() >= 4) || (creationVersion.getMajor() > 8)) { + canAddAction = Case.getCurrentCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE || UserFeatureAccessUtils.canCreateOrModifyMultiUserCases(); } return canAddAction; } diff --git a/Core/src/org/sleuthkit/autopsy/featureaccess/AccessLimiterUtils.java b/Core/src/org/sleuthkit/autopsy/featureaccess/UserFeatureAccessUtils.java similarity index 68% rename from Core/src/org/sleuthkit/autopsy/featureaccess/AccessLimiterUtils.java rename to Core/src/org/sleuthkit/autopsy/featureaccess/UserFeatureAccessUtils.java index 5f352939b5..ce8ac3ea3c 100644 --- a/Core/src/org/sleuthkit/autopsy/featureaccess/AccessLimiterUtils.java +++ b/Core/src/org/sleuthkit/autopsy/featureaccess/UserFeatureAccessUtils.java @@ -23,27 +23,28 @@ import java.nio.file.Paths; import org.sleuthkit.autopsy.coreutils.PlatformUtil; /** - * Class for methods to check if access should be limited to a feature - * + * Check if access to various features is permitted for the current user. */ -final public class AccessLimiterUtils { +final public class UserFeatureAccessUtils { 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(); /** - * Check if privileges regarding multi-user cases should be restricted. + * Indicates whether or not the current user is allowed to create or modify + * (add or delete data sourcess) multi-user cases. * * @return True if privileges should be restricted, false otherwise. */ - public static boolean limitMultiUserAccess() { - return new File(MULTI_USER_ACCESS_FILE_PATH).exists(); + public static boolean canCreateOrModifyMultiUserCases() { + File accessLimitingFile = new File(MULTI_USER_ACCESS_FILE_PATH); + return !accessLimitingFile.exists(); } /** - * Private constructor for a utility class + * Private constructor to prevent instatiation of this utility class. */ - private AccessLimiterUtils() { - //private constructer left empty intentionally + private UserFeatureAccessUtils() { } + } From fce7fdd0c1b5e8ecf2b699b2cc1295e7777d4199 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 6 Nov 2019 18:04:07 -0500 Subject: [PATCH 16/37] Data source deletion support changes --- .../casemodule/DeleteDataSourceAction.java | 24 ++++++++++++++++++- .../autopsy/datamodel/ImageNode.java | 20 ---------------- .../featureaccess/UserFeatureAccessUtils.java | 4 ++-- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index 25339f2d17..e066101866 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -27,15 +27,20 @@ import javax.swing.SwingWorker; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.featureaccess.UserFeatureAccessUtils; import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber; /** * An Action that allows a user to delete a data source from the current case. + * This action should not always be enabled */ public final class DeleteDataSourceAction extends AbstractAction { private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName()); + private static final int MIN_CASE_DB_SCHEMA_MAJOR_VERSION = 8; + private static final int MIN_CASE_DB_SCHEMA_MINOR_VERSION = 4; private long dataSourceObjectID; private Path caseMetadataFilePath; @@ -50,7 +55,7 @@ public final class DeleteDataSourceAction extends AbstractAction { public DeleteDataSourceAction(Long dataSourceObjectID) { super(Bundle.DeleteDataSourceAction_name_text()); this.dataSourceObjectID = dataSourceObjectID; - this.setEnabled(!IngestManager.getInstance().isIngestRunning()); + this.setEnabled(canBeEnabled()); } @NbBundle.Messages({ @@ -96,6 +101,23 @@ public final class DeleteDataSourceAction extends AbstractAction { } } + /** + * Determines whether or not the delete data source action should be enabled + * + * @return True or false. + */ + private static boolean canBeEnabled() { + boolean canBeEnabled = false; + if (Case.isCaseOpen()) { + CaseDbSchemaVersionNumber creationVersion = Case.getCurrentCase().getSleuthkitCase().getDBSchemaCreationVersion(); + canBeEnabled = ((creationVersion.getMajor() > MIN_CASE_DB_SCHEMA_MAJOR_VERSION) || (creationVersion.getMajor() == MIN_CASE_DB_SCHEMA_MAJOR_VERSION && creationVersion.getMinor() >= MIN_CASE_DB_SCHEMA_MINOR_VERSION)) + && !IngestManager.getInstance().isIngestRunning() + && (Case.getCurrentCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE + || UserFeatureAccessUtils.canCreateOrModifyMultiUserCases()); + } + return canBeEnabled; + } + @Override public DeleteDataSourceAction clone() throws CloneNotSupportedException { DeleteDataSourceAction clonedObject = ((DeleteDataSourceAction) super.clone()); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 3827eaf08a..eb2bcc4585 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -33,8 +33,6 @@ import org.apache.commons.lang3.tuple.Pair; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.featureaccess.UserFeatureAccessUtils; -import org.sleuthkit.autopsy.casemodule.DeleteDataSourceAction; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction; @@ -53,7 +51,6 @@ import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.VirtualDirectory; import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException; -import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber; import org.sleuthkit.datamodel.Tag; /** @@ -122,9 +119,6 @@ public class ImageNode extends AbstractContentNode { actionsList.add(new RunIngestModulesAction(Collections.singletonList(content))); actionsList.add(new NewWindowViewAction( NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this)); - if (canAddDeleteDataSourceAction()) { - actionsList.add(new DeleteDataSourceAction(content.getId())); - } return actionsList.toArray(new Action[0]); } @@ -212,20 +206,6 @@ public class ImageNode extends AbstractContentNode { return getClass().getName(); } - /** - * Determines whether or not the delete data source action can be added. - * - * @return True or false. - */ - private Boolean canAddDeleteDataSourceAction() { - boolean canAddAction = false; - CaseDbSchemaVersionNumber creationVersion = Case.getCurrentCase().getSleuthkitCase().getDBSchemaCreationVersion(); - if ((creationVersion.getMajor() == 8 && creationVersion.getMinor() >= 4) || (creationVersion.getMajor() > 8)) { - canAddAction = Case.getCurrentCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE || UserFeatureAccessUtils.canCreateOrModifyMultiUserCases(); - } - return canAddAction; - } - /* * This property change listener refreshes the tree when a new file is * carved out of this image (i.e, the image is being treated as raw bytes diff --git a/Core/src/org/sleuthkit/autopsy/featureaccess/UserFeatureAccessUtils.java b/Core/src/org/sleuthkit/autopsy/featureaccess/UserFeatureAccessUtils.java index ce8ac3ea3c..384f0ce4f9 100644 --- a/Core/src/org/sleuthkit/autopsy/featureaccess/UserFeatureAccessUtils.java +++ b/Core/src/org/sleuthkit/autopsy/featureaccess/UserFeatureAccessUtils.java @@ -32,9 +32,9 @@ final public class UserFeatureAccessUtils { /** * Indicates whether or not the current user is allowed to create or modify - * (add or delete data sourcess) multi-user cases. + * (add or delete data sources) multi-user cases. * - * @return True if privileges should be restricted, false otherwise. + * @return True or false. */ public static boolean canCreateOrModifyMultiUserCases() { File accessLimitingFile = new File(MULTI_USER_ACCESS_FILE_PATH); From dca749187882e782e6997b632a9e5787c4cb9316 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 6 Nov 2019 18:08:29 -0500 Subject: [PATCH 17/37] Data source deletion support changes --- .../autopsy/casemodule/DeleteDataSourceAction.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index e066101866..a25a596f9e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -102,15 +102,15 @@ public final class DeleteDataSourceAction extends AbstractAction { } /** - * Determines whether or not the delete data source action should be enabled + * Determines whether or not the delete data source action can be enabled. * * @return True or false. */ private static boolean canBeEnabled() { boolean canBeEnabled = false; if (Case.isCaseOpen()) { - CaseDbSchemaVersionNumber creationVersion = Case.getCurrentCase().getSleuthkitCase().getDBSchemaCreationVersion(); - canBeEnabled = ((creationVersion.getMajor() > MIN_CASE_DB_SCHEMA_MAJOR_VERSION) || (creationVersion.getMajor() == MIN_CASE_DB_SCHEMA_MAJOR_VERSION && creationVersion.getMinor() >= MIN_CASE_DB_SCHEMA_MINOR_VERSION)) + CaseDbSchemaVersionNumber version = Case.getCurrentCase().getSleuthkitCase().getDBSchemaCreationVersion(); + canBeEnabled = ((version.getMajor() > MIN_CASE_DB_SCHEMA_MAJOR_VERSION) || (version.getMajor() == MIN_CASE_DB_SCHEMA_MAJOR_VERSION && version.getMinor() >= MIN_CASE_DB_SCHEMA_MINOR_VERSION)) && !IngestManager.getInstance().isIngestRunning() && (Case.getCurrentCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE || UserFeatureAccessUtils.canCreateOrModifyMultiUserCases()); From 982e40a8df3c4251a6890a4a7e1c4e88a97383ff Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 6 Nov 2019 18:39:29 -0500 Subject: [PATCH 18/37] Data source deletion support changes --- .../sleuthkit/autopsy/casemodule/Case.java | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 16fe5ad187..6aa21e8a02 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -159,7 +159,7 @@ public class Case { private static volatile Case currentCase; private final CaseMetadata metadata; private volatile ExecutorService caseLockingExecutor; - private CoordinationService.Lock caseDirLock; + private CoordinationService.Lock caseLock; private SleuthkitCase caseDb; private CollaborationMonitor collaborationMonitor; private Services caseServices; @@ -1755,7 +1755,8 @@ public class Case { } /** - * Performs a case action. + * Performs a case action after acquiring a coordination service case lock. + * The close() method must eventually be called to release the lock. * * @param progressIndicatorTitle A title for the progress indicator for the * case action. @@ -1808,9 +1809,7 @@ public class Case { /* * A case action is always done by creating a task running in the same - * non-UI thread that will be used to close the case, so a - * single-threaded executor service is created here and saved as case - * state (must be volatile for cancellation to work). For both + * non-UI thread that will be used to close the case. For both * single-user and mulit-user cases, this supports cancelling the case * action by cancelling the task. If the case is a multi-user case, this * also ensures that the case lock is released in the same thread in @@ -1828,14 +1827,14 @@ public class Case { * create/open/upgrade/close the case resources. */ progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources()); - acquireCaseLock(caseLockType, metadata.getCaseDirectory()); + acquireCaseLock(caseLockType); try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) { if (null == resourcesLock) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock()); } caseAction.execute(progressIndicator, additionalParams); } catch (CaseActionException ex) { - releaseSharedCaseDirLock(getMetadata().getCaseDirectory()); + releaseCaseLock(); throw ex; } } @@ -1991,10 +1990,8 @@ public class Case { } /** - * A case action (interface CaseAction) 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. + * A case action (interface CaseAction) that opens a case, deletes a + * data source from the case, and closes the case. * * @param progressIndicator A progress indicator. * @param additionalParams An Object that holds any additional parameters @@ -2488,9 +2485,9 @@ public class Case { /* * Closing a case is always done in the same non-UI thread that * opened/created the case. If the case is a multi-user case, this - * ensures that case directory lock that is held as long as the case is - * open is released in the same thread in which it was acquired, as is - * required by the coordination service. + * ensures that case lock that is held as long as the case is open is + * released in the same thread in which it was acquired, as is required + * by the coordination service. */ Future future = caseLockingExecutor.submit(() -> { if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { @@ -2512,7 +2509,7 @@ public class Case { * Always release the case directory lock that was acquired * when the case was opened. */ - releaseSharedCaseDirLock(metadata.getCaseDirectory()); // RJCTODO: Rename + releaseCaseLock(); } } return null; @@ -2637,18 +2634,17 @@ public class Case { /** * Acquires a case (case directory) lock for the current case. * - * @param caseDir The full path of the case directory. - * * @throws CaseActionException If the lock cannot be acquired. */ @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory"}) - private void acquireCaseLock(CaseLockType lockType, String caseDir) throws CaseActionException { + private void acquireCaseLock(CaseLockType lockType) throws CaseActionException { + String caseDir = metadata.getCaseDirectory(); try { CoordinationService coordinationService = CoordinationService.getInstance(); - caseDirLock = lockType == CaseLockType.SHARED + caseLock = 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 (caseLock == null) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock()); } } catch (InterruptedException | CoordinationServiceException ex) { @@ -2657,17 +2653,15 @@ public class Case { } /** - * Releases a shared case directory lock for the current case. - * - * @param caseDir The full path of the case directory. + * Releases a case (case directory) lock for the current case. */ - private void releaseSharedCaseDirLock(String caseDir) { - if (caseDirLock != null) { + private void releaseCaseLock() { + if (caseLock != null) { try { - caseDirLock.release(); - caseDirLock = null; + caseLock.release(); + caseLock = null; } catch (CoordinationService.CoordinationServiceException ex) { - logger.log(Level.SEVERE, String.format("Failed to release shared case directory lock for %s", caseDir), ex); + logger.log(Level.SEVERE, String.format("Failed to release shared case directory lock for %s", getMetadata().getCaseDirectory()), ex); } } } From c93d7d59171b827545ebad44c923c07a55b1ab75 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Thu, 7 Nov 2019 14:00:55 -0500 Subject: [PATCH 19/37] Data source deletion support changes --- .../casemodule/Bundle.properties-MERGED | 2 +- .../sleuthkit/autopsy/casemodule/Case.java | 165 ++++++++++-------- 2 files changed, 89 insertions(+), 78 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 9286a181cd..0d96dc1eca 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -9,7 +9,7 @@ Case.deleteCaseConfirmationDialog.title=Delete Current Case? # {0} - exception message Case.deleteCaseFailureMessageBox.message=Error deleting case: {0} Case.deleteCaseFailureMessageBox.title=Failed to Delete Case -Case.exceptionMessage.cancelledByUser=Cancelled by user. +Case.exceptionMessage.cancelled=Cancelled. 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.cannotLocateMainWindow=Cannot locate main application window diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 6aa21e8a02..6ca6d5ce4d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -158,7 +158,7 @@ public class Case { private static volatile Frame mainFrame; private static volatile Case currentCase; private final CaseMetadata metadata; - private volatile ExecutorService caseLockingExecutor; + private volatile ExecutorService caseActionExecutor; private CoordinationService.Lock caseLock; private SleuthkitCase caseDb; private CollaborationMonitor collaborationMonitor; @@ -756,13 +756,26 @@ public class Case { }) public static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException { synchronized (caseActionSerializationLock) { + /* + * Close the current case to release the shared case lock. + */ if (null == currentCase) { return; } CaseMetadata caseMetadata = currentCase.getMetadata(); closeCurrentCase(); + + /* + * Re-open the case with an exclusive case lock, delete the data + * source, and close the case again, releasing the exclusive case + * lock. + */ Case theCase = new Case(caseMetadata); - theCase.doCaseAction(Bundle.Case_progressIndicatorTitle_deletingDataSource(), theCase::deleteDataSource, CaseLockType.EXCLUSIVE, false, dataSourceObjectID); + theCase.doOpenCaseAction(Bundle.Case_progressIndicatorTitle_deletingDataSource(), theCase::deleteDataSource, CaseLockType.EXCLUSIVE, false, dataSourceObjectID); + + /* + * Re-open the case with a shared case lock. + */ openAsCurrentCase(new Case(caseMetadata), false); } } @@ -851,12 +864,12 @@ public class Case { CaseAction openCaseAction; if (isNewCase) { progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_creatingCase(); - openCaseAction = newCurrentCase::createCase; + openCaseAction = newCurrentCase::create; } else { progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_openingCase(); - openCaseAction = newCurrentCase::openCase; + openCaseAction = newCurrentCase::open; } - newCurrentCase.doCaseAction(progressIndicatorTitle, openCaseAction, CaseLockType.SHARED, true, null); + newCurrentCase.doOpenCaseAction(progressIndicatorTitle, openCaseAction, CaseLockType.SHARED, true, null); 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 if (RuntimeProperties.runningWithGUI()) { @@ -1750,13 +1763,24 @@ public class Case { private Case(CaseMetadata caseMetaData) { metadata = caseMetaData; TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName())); - caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory); + caseActionExecutor = Executors.newSingleThreadExecutor(threadFactory); tskEventForwarder = new TSKCaseRepublisher(); } /** - * Performs a case action after acquiring a coordination service case lock. - * The close() method must eventually be called to release the lock. + * Performs an open case action. If the case is a multi-user case, the + * action is done after acquiring a coordination service case lock. The case + * lock must eventually be released in the same thread in which it was + * acquired, as required by the coordination service. The open case action + * is therefore done in a task running in the single thread in the case + * action executor. + * + * IMPORTANT: If an open case action for a multi-user case is terminated + * because an exception is thrown or the action is cancelled, the action is + * responsible for releasing the case lock while still running in the case + * action executor thread. This is required because this method handles + * interrupts, cancellation exceptions, and execution exceptions by assuming + * the case will be closed and shutting down the case action executor. * * @param progressIndicatorTitle A title for the progress indicator for the * case action. @@ -1778,13 +1802,13 @@ public class Case { "Case.progressMessage.preparing=Preparing...", "Case.progressMessage.preparingToOpenCaseResources=Preparing to open case resources.
This may take time if another user is upgrading the case.", "Case.progressMessage.cancelling=Cancelling...", - "Case.exceptionMessage.cancelledByUser=Cancelled by user.", + "Case.exceptionMessage.cancelled=Cancelled.", "# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}" }) - private void doCaseAction(String progressIndicatorTitle, CaseAction caseAction, CaseLockType caseLockType, boolean allowCancellation, Object additionalParams) throws CaseActionException { + private void doOpenCaseAction(String progressIndicatorTitle, CaseAction caseAction, CaseLockType caseLockType, boolean allowCancellation, Object additionalParams) throws CaseActionException { /* - * Create and start either a GUI progress indicator or a logging - * progress indicator. + * Create and start either a GUI progress indicator (with or without a + * cancel button) or a logging progress indicator. */ CancelButtonListener cancelButtonListener = null; ProgressIndicator progressIndicator; @@ -1808,24 +1832,15 @@ public class Case { progressIndicator.start(Bundle.Case_progressMessage_preparing()); /* - * A case action is always done by creating a task running in the same - * non-UI thread that will be used to close the case. For both - * single-user and mulit-user cases, this supports cancelling the case - * action by cancelling the task. If the case is a multi-user case, this - * also ensures that the case lock is released in the same thread in - * which it was acquired, which is required by the coordination service. + * Do the case action in the single thread in the case action executor. + * If the case is a multi-user case, a case lock is acquired and held + * until explictly released and an exclusive case resources lock is + * aquired and held for the duration of the action. */ - Future future = caseLockingExecutor.submit(() -> { + Future future = caseActionExecutor.submit(() -> { if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { caseAction.execute(progressIndicator, additionalParams); } else { - /* - * First, acquire a case lock that will be held as long as this - * node has this case open. This will prevent deletion of the - * case by another node. Next, acquire an exclusive case - * resources lock to ensure only one node at a time can - * create/open/upgrade/close the case resources. - */ progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources()); acquireCaseLock(caseLockType); try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) { @@ -1851,36 +1866,25 @@ public class Case { future.get(); } catch (InterruptedException discarded) { /* - * The thread this method is running in has been interrupted. Cancel - * the case action task, wait for it to finish, and shut down the - * executor. This can be done safely because if the task is - * completed with a cancellation condition, the case will have been - * closed and the case lock will have been released. + * The thread this method is running in has been interrupted. */ if (null != cancelButtonListener) { cancelButtonListener.actionPerformed(null); } else { future.cancel(true); } - ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); // RJCTODO + ThreadUtils.shutDownTaskExecutor(caseActionExecutor); } catch (CancellationException discarded) { /* - * The case action task has been cancelled. Wait for it to finish, - * and shut down the executor. This can be done safely because if - * the task is completed with a cancellation condition, the case - * will have been closed and the case lock will have been released. + * The case action has been cancelled. */ - ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); // RJCTODO - throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); + ThreadUtils.shutDownTaskExecutor(caseActionExecutor); + throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled()); } catch (ExecutionException ex) { /* - * The case action task has thrown an exception. Wait for it to - * finish, and shut down the executor. This can be done safely - * because if the task is completed with an execution condition, the - * case will have been closed and the case lock will have been - * released. + * The case action has thrown an exception. */ - ThreadUtils.shutDownTaskExecutor(caseLockingExecutor); // RJCTODO + ThreadUtils.shutDownTaskExecutor(caseActionExecutor); // RJCTODO: Log error ion the thread where a stack trace can be recorded throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex); } finally { progressIndicator.finish(); @@ -1902,35 +1906,38 @@ public class Case { * message and may be a wrapper for a * lower-level exception. */ - private Void createCase(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { + private Void create(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { assert (additionalParams == null); try { - checkForUserCancellation(); + checkForCancellation(); createCaseDirectoryIfDoesNotExist(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); switchLoggingToCaseLogsDirectory(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); saveCaseMetadataToFile(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); createCaseNodeData(progressIndicator); - checkForUserCancellation(); - checkForUserCancellation(); + checkForCancellation(); + checkForCancellation(); createCaseDatabase(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); openCaseLevelServices(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); openAppServiceCaseResources(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); 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. + * Cancellation or failure. Log the exception now, while running in + * the case action executor with the stack trace available, then + * 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. */ + logger.log(Level.WARNING, "Failed to open the case", ex); // RJCTODO try { Thread.sleep(1); } catch (InterruptedException discarded) { @@ -1954,32 +1961,35 @@ public class Case { * message and may be a wrapper for a * lower-level exception. */ - private Void openCase(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { + private Void open(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { assert (additionalParams == null); try { - checkForUserCancellation(); + checkForCancellation(); switchLoggingToCaseLogsDirectory(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); updateCaseNodeData(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); deleteTempfilesFromCaseDirectory(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); openCaseDataBase(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); openCaseLevelServices(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); openAppServiceCaseResources(progressIndicator); - checkForUserCancellation(); + checkForCancellation(); 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. + * Cancellation or failure. Log the exception now, while running in + * the case action executor with the stack trace available, then + * 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. */ + logger.log(Level.WARNING, "Failed to open the case", ex); // RJCTODO try { Thread.sleep(1); } catch (InterruptedException discarded) { @@ -1990,8 +2000,8 @@ public class Case { } /** - * A case action (interface CaseAction) that opens a case, deletes a - * data source from the case, and closes the case. + * A case action (interface CaseAction) that opens a case, deletes + * a data source from the case, and closes the case. * * @param progressIndicator A progress indicator. * @param additionalParams An Object that holds any additional parameters @@ -2010,7 +2020,7 @@ public class Case { }) Void deleteDataSource(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { assert (additionalParams instanceof Long); - openCase(progressIndicator, null); + open(progressIndicator, null); try { progressIndicator.progress(Bundle.Case_progressMessage_deletingDataSource()); Long dataSourceObjectID = (Long) additionalParams; @@ -2083,7 +2093,7 @@ public class Case { * interrupted, assumes interrupt was * due to a user action. */ - private static void checkForUserCancellation() throws CaseActionCancelledException { + private static void checkForCancellation() throws CaseActionCancelledException { if (Thread.currentThread().isInterrupted()) { throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); } @@ -2424,7 +2434,7 @@ public class Case { ThreadUtils.shutDownTaskExecutor(executor); appServiceProgressIndicator.finish(); } - checkForUserCancellation(); + checkForCancellation(); } } @@ -2449,7 +2459,7 @@ public class Case { progressIndicator.progress(Bundle.Case_progressMessage_settingUpNetworkCommunications()); try { eventPublisher.openRemoteEventChannel(String.format(EVENT_CHANNEL_NAME, metadata.getCaseName())); - checkForUserCancellation(); + checkForCancellation(); collaborationMonitor = new CollaborationMonitor(metadata.getCaseName()); } catch (AutopsyEventException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenRemoteEventChannel(ex.getLocalizedMessage()), ex); @@ -2489,7 +2499,7 @@ public class Case { * released in the same thread in which it was acquired, as is required * by the coordination service. */ - Future future = caseLockingExecutor.submit(() -> { + Future future = caseActionExecutor.submit(() -> { if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { close(progressIndicator); } else { @@ -2527,6 +2537,7 @@ public class Case { } catch (ExecutionException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex); } finally { + ThreadUtils.shutDownTaskExecutor(caseActionExecutor); progressIndicator.finish(); } } From 3b8e83620405058c704e24c3bd3faf863390afd2 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Thu, 7 Nov 2019 14:11:00 -0500 Subject: [PATCH 20/37] Data source deletion support changes --- .../org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED | 2 +- .../sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 0d96dc1eca..7e2c00ffb0 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -123,7 +123,7 @@ CTL_CaseDetailsAction=Case Details CTL_CaseDeleteAction=Delete Case CTL_CaseOpenAction=Open 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?\n Note that the case will be closed and re-opened several times to complete the deletion. +DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?\n Note that the case will be closed and re-opened during the deletion. DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open 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 diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index a25a596f9e..0929509427 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -59,7 +59,7 @@ public final class DeleteDataSourceAction extends AbstractAction { } @NbBundle.Messages({ - "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?\n Note that the case will be closed and re-opened several times to complete the deletion.", + "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?\n Note that the case will be closed and re-opened during the deletion.", "DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source.\nPlease see the application log for details.", "DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case." }) From 95769f9bc6edb03940540eb53b79f59713f77aa9 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Thu, 7 Nov 2019 14:14:32 -0500 Subject: [PATCH 21/37] Data source deletion support changes --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 4 ++-- .../autopsy/casemodule/DeleteDataSourceAction.java | 6 +++--- .../sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java | 4 ++-- ...{UserFeatureAccessUtils.java => FeatureAccessUtils.java} | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) rename Core/src/org/sleuthkit/autopsy/featureaccess/{UserFeatureAccessUtils.java => FeatureAccessUtils.java} (90%) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 6ca6d5ce4d..067a1084d4 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -18,7 +18,7 @@ */ package org.sleuthkit.autopsy.casemodule; -import org.sleuthkit.autopsy.featureaccess.UserFeatureAccessUtils; +import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils; import com.google.common.annotations.Beta; import com.google.common.eventbus.Subscribe; import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData; @@ -1102,7 +1102,7 @@ public class Case { /* * Enable the case-specific actions. */ - CallableSystemAction.get(AddImageAction.class).setEnabled(Case.getCurrentCase().getMetadata().getCaseType() == CaseType.SINGLE_USER_CASE || UserFeatureAccessUtils.canCreateOrModifyMultiUserCases()); + CallableSystemAction.get(AddImageAction.class).setEnabled(Case.getCurrentCase().getMetadata().getCaseType() == CaseType.SINGLE_USER_CASE || FeatureAccessUtils.canCreateOrModifyMultiUserCases()); CallableSystemAction.get(CaseCloseAction.class).setEnabled(true); CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true); CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index 0929509427..e59510ae9a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -27,7 +27,7 @@ import javax.swing.SwingWorker; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.featureaccess.UserFeatureAccessUtils; +import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber; @@ -59,7 +59,7 @@ public final class DeleteDataSourceAction extends AbstractAction { } @NbBundle.Messages({ - "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?\n Note that the case will be closed and re-opened during the deletion.", + "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?\nNote that the case will be closed and re-opened during the deletion.", "DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source.\nPlease see the application log for details.", "DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case." }) @@ -113,7 +113,7 @@ public final class DeleteDataSourceAction extends AbstractAction { canBeEnabled = ((version.getMajor() > MIN_CASE_DB_SCHEMA_MAJOR_VERSION) || (version.getMajor() == MIN_CASE_DB_SCHEMA_MAJOR_VERSION && version.getMinor() >= MIN_CASE_DB_SCHEMA_MINOR_VERSION)) && !IngestManager.getInstance().isIngestRunning() && (Case.getCurrentCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE - || UserFeatureAccessUtils.canCreateOrModifyMultiUserCases()); + || FeatureAccessUtils.canCreateOrModifyMultiUserCases()); } return canBeEnabled; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java index 76492893b2..2f39768336 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java @@ -18,7 +18,7 @@ */ package org.sleuthkit.autopsy.casemodule; -import org.sleuthkit.autopsy.featureaccess.UserFeatureAccessUtils; +import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils; import java.awt.Component; import org.openide.util.NbBundle; @@ -62,7 +62,7 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener { */ void readSettings() { caseNameTextField.setText(""); - if (UserPreferences.getIsMultiUserModeEnabled() && UserFeatureAccessUtils.canCreateOrModifyMultiUserCases()) { + if (UserPreferences.getIsMultiUserModeEnabled() && FeatureAccessUtils.canCreateOrModifyMultiUserCases()) { multiUserCaseRadioButton.setEnabled(true); multiUserCaseRadioButton.setSelected(true); } else { diff --git a/Core/src/org/sleuthkit/autopsy/featureaccess/UserFeatureAccessUtils.java b/Core/src/org/sleuthkit/autopsy/featureaccess/FeatureAccessUtils.java similarity index 90% rename from Core/src/org/sleuthkit/autopsy/featureaccess/UserFeatureAccessUtils.java rename to Core/src/org/sleuthkit/autopsy/featureaccess/FeatureAccessUtils.java index 384f0ce4f9..8cdfea7910 100644 --- a/Core/src/org/sleuthkit/autopsy/featureaccess/UserFeatureAccessUtils.java +++ b/Core/src/org/sleuthkit/autopsy/featureaccess/FeatureAccessUtils.java @@ -25,7 +25,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil; /** * Check if access to various features is permitted for the current user. */ -final public class UserFeatureAccessUtils { +final public class FeatureAccessUtils { 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(); @@ -42,9 +42,9 @@ final public class UserFeatureAccessUtils { } /** - * Private constructor to prevent instatiation of this utility class. + * Private constructor to prevent instantiation of this utility class. */ - private UserFeatureAccessUtils() { + private FeatureAccessUtils() { } } From 4183e948ba55b66a1502c31dc6d72ca4fe699caf Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 8 Nov 2019 13:21:41 -0500 Subject: [PATCH 22/37] Data source deletion support changes --- .../sleuthkit/autopsy/casemodule/Bundle.properties-MERGED | 2 +- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 7e2c00ffb0..2115b32d9e 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -123,7 +123,7 @@ CTL_CaseDetailsAction=Case Details CTL_CaseDeleteAction=Delete Case CTL_CaseOpenAction=Open 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?\n Note that the case will be closed and re-opened during the deletion. +DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?\nNote that the case will be closed and re-opened during the deletion. DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open 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 diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 067a1084d4..a4d5f4091b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -756,12 +756,13 @@ public class Case { }) public static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException { synchronized (caseActionSerializationLock) { - /* - * Close the current case to release the shared case lock. - */ if (null == currentCase) { return; } + + /* + * Close the current case to release the shared case lock. + */ CaseMetadata caseMetadata = currentCase.getMetadata(); closeCurrentCase(); From 5898e027ecf4dfe8306d813519ebd57d06ed4e11 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 8 Nov 2019 16:30:29 -0500 Subject: [PATCH 23/37] Data source deletion support changes --- .../casemodule/Bundle.properties-MERGED | 1 + .../sleuthkit/autopsy/casemodule/Case.java | 2 +- .../casemodule/DeleteDataSourceAction.java | 27 +++------ .../casemodule/NewCaseVisualPanel1.java | 2 +- .../featureaccess/FeatureAccessUtils.java | 59 +++++++++++++++++-- 5 files changed, 64 insertions(+), 27 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 2115b32d9e..8451a951cf 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -127,6 +127,7 @@ DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delet DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open 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 +DeleteDataSourceAction.warningDialog.message=Data sources cannot be deleted when ingest is running. EditOptionalCasePropertiesPanel.cancelButton.text=Cancel EditOptionalCasePropertiesPanel.saveButton.text=Save GeneralFilter.encaseImageDesc.text=Encase Images (*.e01) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index a4d5f4091b..0ce03c6052 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -1103,7 +1103,7 @@ public class Case { /* * Enable the case-specific actions. */ - CallableSystemAction.get(AddImageAction.class).setEnabled(Case.getCurrentCase().getMetadata().getCaseType() == CaseType.SINGLE_USER_CASE || FeatureAccessUtils.canCreateOrModifyMultiUserCases()); + CallableSystemAction.get(AddImageAction.class).setEnabled(FeatureAccessUtils.canAddDataSources()); CallableSystemAction.get(CaseCloseAction.class).setEnabled(true); CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true); CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index e59510ae9a..b495fc3728 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -39,8 +39,6 @@ public final class DeleteDataSourceAction extends AbstractAction { private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName()); - private static final int MIN_CASE_DB_SCHEMA_MAJOR_VERSION = 8; - private static final int MIN_CASE_DB_SCHEMA_MINOR_VERSION = 4; private long dataSourceObjectID; private Path caseMetadataFilePath; @@ -55,16 +53,22 @@ public final class DeleteDataSourceAction extends AbstractAction { public DeleteDataSourceAction(Long dataSourceObjectID) { super(Bundle.DeleteDataSourceAction_name_text()); this.dataSourceObjectID = dataSourceObjectID; - this.setEnabled(canBeEnabled()); + this.setEnabled(FeatureAccessUtils.canDeleteDataSources()); } @NbBundle.Messages({ + "DeleteDataSourceAction.warningDialog.message=Data sources cannot be deleted when ingest is running.", "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?\nNote that the case will be closed and re-opened during the deletion.", "DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source.\nPlease see the application log for details.", "DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case." }) @Override public void actionPerformed(ActionEvent event) { + if (IngestManager.getInstance().isIngestRunning()) { + MessageNotifyUtil.Message.warn(Bundle.DeleteDataSourceAction_warningDialog_message()); + return; + } + if (MessageNotifyUtil.Message.confirm(Bundle.DeleteDataSourceAction_confirmationDialog_message())) { new SwingWorker() { @@ -101,23 +105,6 @@ public final class DeleteDataSourceAction extends AbstractAction { } } - /** - * Determines whether or not the delete data source action can be enabled. - * - * @return True or false. - */ - private static boolean canBeEnabled() { - boolean canBeEnabled = false; - if (Case.isCaseOpen()) { - CaseDbSchemaVersionNumber version = Case.getCurrentCase().getSleuthkitCase().getDBSchemaCreationVersion(); - canBeEnabled = ((version.getMajor() > MIN_CASE_DB_SCHEMA_MAJOR_VERSION) || (version.getMajor() == MIN_CASE_DB_SCHEMA_MAJOR_VERSION && version.getMinor() >= MIN_CASE_DB_SCHEMA_MINOR_VERSION)) - && !IngestManager.getInstance().isIngestRunning() - && (Case.getCurrentCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE - || FeatureAccessUtils.canCreateOrModifyMultiUserCases()); - } - return canBeEnabled; - } - @Override public DeleteDataSourceAction clone() throws CloneNotSupportedException { DeleteDataSourceAction clonedObject = ((DeleteDataSourceAction) super.clone()); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java index 2f39768336..41c1697a76 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java @@ -62,7 +62,7 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener { */ void readSettings() { caseNameTextField.setText(""); - if (UserPreferences.getIsMultiUserModeEnabled() && FeatureAccessUtils.canCreateOrModifyMultiUserCases()) { + if (FeatureAccessUtils.canCreateMultiUserCases()) { multiUserCaseRadioButton.setEnabled(true); multiUserCaseRadioButton.setSelected(true); } else { diff --git a/Core/src/org/sleuthkit/autopsy/featureaccess/FeatureAccessUtils.java b/Core/src/org/sleuthkit/autopsy/featureaccess/FeatureAccessUtils.java index 8cdfea7910..d646147c61 100644 --- a/Core/src/org/sleuthkit/autopsy/featureaccess/FeatureAccessUtils.java +++ b/Core/src/org/sleuthkit/autopsy/featureaccess/FeatureAccessUtils.java @@ -20,15 +20,64 @@ package org.sleuthkit.autopsy.featureaccess; import java.io.File; import java.nio.file.Paths; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.PlatformUtil; +import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber; /** - * Check if access to various features is permitted for the current user. + * Check if access to various features is permitted for the current user and the + * current case, if any. */ final public class FeatureAccessUtils { - 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 MULTIUSER_CASE_RESTRICTED_FILE_NAME = "mualimit"; // NON-NLS + private final static String MULTIUSER_CASE_RESTRICTED_FILE_PATH = Paths.get(PlatformUtil.getUserConfigDirectory(), MULTIUSER_CASE_RESTRICTED_FILE_NAME).toString(); + private static final int DATA_SRC_DEL_MIN_DB_MAJOR_VER = 8; + private static final int DATA_SRC_DEL_MIN_DB_MINOR_VER = 4; + + /** + * Indicates whether or not a user can create multi-user cases. + * + * @return True or false. + */ + public static boolean canCreateMultiUserCases() { + return UserPreferences.getIsMultiUserModeEnabled() && multiUserCaseRestrictionsFileAbsent(); + } + + /** + * Indicates whether or not a user can add data sources to a case. + * + * @return True or false. + */ + public static boolean canAddDataSources() { + return currentCaseIsSingleUserCase() || multiUserCaseRestrictionsFileAbsent(); + } + + /** + * Indicates whether or not a user can delete data sources from a case. + * + * @return True or false. + */ + public static boolean canDeleteDataSources() { + boolean dataSourceDeletionAllowed = false; + if (Case.isCaseOpen()) { + CaseDbSchemaVersionNumber version = Case.getCurrentCase().getSleuthkitCase().getDBSchemaCreationVersion(); + dataSourceDeletionAllowed + = ((version.getMajor() > DATA_SRC_DEL_MIN_DB_MAJOR_VER) || (version.getMajor() == DATA_SRC_DEL_MIN_DB_MAJOR_VER && version.getMinor() >= DATA_SRC_DEL_MIN_DB_MINOR_VER)) + && (currentCaseIsSingleUserCase() || multiUserCaseRestrictionsFileAbsent()); + } + return dataSourceDeletionAllowed; + } + + /** + * Indicates whether or not the current case is a single-user case. + * + * @return True or false. + */ + private static boolean currentCaseIsSingleUserCase() { + return Case.isCaseOpen() && Case.getCurrentCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE; + } /** * Indicates whether or not the current user is allowed to create or modify @@ -36,8 +85,8 @@ final public class FeatureAccessUtils { * * @return True or false. */ - public static boolean canCreateOrModifyMultiUserCases() { - File accessLimitingFile = new File(MULTI_USER_ACCESS_FILE_PATH); + public static boolean multiUserCaseRestrictionsFileAbsent() { + File accessLimitingFile = new File(MULTIUSER_CASE_RESTRICTED_FILE_PATH); return !accessLimitingFile.exists(); } From b0138680d02623e2a9e8ed2419470adeb9d5fbf0 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 8 Nov 2019 16:43:15 -0500 Subject: [PATCH 24/37] Data source deletion support changes --- Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java | 7 +++---- .../core.jar/org/netbeans/core/startup/Bundle.properties | 2 +- .../org/netbeans/core/windows/view/ui/Bundle.properties | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index eb2bcc4585..9e4ec9eee8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -113,12 +113,11 @@ public class ImageNode extends AbstractContentNode { actionsList.add(a); } actionsList.addAll(ExplorerNodeActionVisitor.getActions(content)); - actionsList.add(new FileSearchAction( - Bundle.ImageNode_getActions_openFileSearchByAttr_text())); + actionsList.add(new FileSearchAction(Bundle.ImageNode_getActions_openFileSearchByAttr_text())); actionsList.add(new ViewSummaryInformationAction(content.getId())); actionsList.add(new RunIngestModulesAction(Collections.singletonList(content))); - actionsList.add(new NewWindowViewAction( - NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this)); + actionsList.add(new NewWindowViewAction(NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this)); + actionsList.add(new DeleteDataSourceAction(content.getId())); return actionsList.toArray(new Action[0]); } diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index 6e10156aef..cd78d5c461 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Mon, 28 Oct 2019 16:22:16 -0400 +#Fri, 08 Nov 2019 16:32:05 -0500 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 79b6f9b6fe..721ab292cd 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Mon, 28 Oct 2019 16:22:16 -0400 +#Fri, 08 Nov 2019 16:32:05 -0500 CTL_MainWindow_Title=Autopsy 4.13.0 CTL_MainWindow_Title_No_Project=Autopsy 4.13.0 From 16e94f6dec77248f054d0bd6bf60449bc025cf14 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 11 Nov 2019 15:54:19 -0500 Subject: [PATCH 25/37] Data source deletion support changes --- .../autopsy/casemodule/Bundle.properties-MERGED | 3 ++- .../org/sleuthkit/autopsy/casemodule/Case.java | 15 ++++++++------- .../casemodule/DeleteDataSourceAction.java | 15 +++++++-------- .../sleuthkit/autopsy/datamodel/ImageNode.java | 1 + .../autopsy/featureaccess/FeatureAccessUtils.java | 3 +++ 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 8451a951cf..2cd4dd414d 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -125,7 +125,8 @@ CTL_CaseOpenAction=Open 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?\nNote that the case will be closed and re-opened during the deletion. DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case. -DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source.\nPlease see the application log for details. +# {0} - exception message +DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source:\n{0}\nPlease see the application log for details. DeleteDataSourceAction.name.text=Delete Data Source DeleteDataSourceAction.warningDialog.message=Data sources cannot be deleted when ingest is running. EditOptionalCasePropertiesPanel.cancelButton.text=Cancel diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 0ce03c6052..717fbc97a2 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -138,8 +138,9 @@ import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException; */ public class Case { - private static final int DIR_LOCK_TIMOUT_HOURS = 12; - private static final int RESOURCES_LOCK_TIMOUT_HOURS = 12; + private static final int SHARED_CASE_LOCK_TIMEOUT_SECONDS = 30; + private static final int EXCLUSIVE_CASE_LOCK_TIMEOUT_MINS = 1; + private static final int CASE_RESOURCES_LOCK_TIMEOUT_HOURS = 1; private static final String SINGLE_USER_CASE_DB_NAME = "autopsy.db"; private static final String EVENT_CHANNEL_NAME = "%s-Case-Events"; //NON-NLS private static final String CACHE_FOLDER = "Cache"; //NON-NLS @@ -1036,10 +1037,10 @@ public class Case { try { Path caseDirPath = Paths.get(caseDir); String resourcesNodeName = CoordinationServiceUtils.getCaseResourcesNodePath(caseDirPath); - Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, resourcesNodeName, RESOURCES_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); + Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, resourcesNodeName, CASE_RESOURCES_LOCK_TIMEOUT_HOURS, TimeUnit.HOURS); return lock; } catch (InterruptedException ex) { - throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); + throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled()); } catch (CoordinationServiceException ex) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock(), ex); } @@ -2096,7 +2097,7 @@ public class Case { */ private static void checkForCancellation() throws CaseActionCancelledException { if (Thread.currentThread().isInterrupted()) { - throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); + throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled()); } } @@ -2654,8 +2655,8 @@ public class Case { try { CoordinationService coordinationService = CoordinationService.getInstance(); caseLock = 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); + ? coordinationService.tryGetSharedLock(CategoryNode.CASES, caseDir, SHARED_CASE_LOCK_TIMEOUT_SECONDS, TimeUnit.MINUTES) + : coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseDir, EXCLUSIVE_CASE_LOCK_TIMEOUT_MINS, TimeUnit.SECONDS); if (caseLock == null) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock()); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index b495fc3728..cb4c02d51a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -29,11 +29,9 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils; import org.sleuthkit.autopsy.ingest.IngestManager; -import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber; /** * An Action that allows a user to delete a data source from the current case. - * This action should not always be enabled */ public final class DeleteDataSourceAction extends AbstractAction { @@ -59,7 +57,7 @@ public final class DeleteDataSourceAction extends AbstractAction { @NbBundle.Messages({ "DeleteDataSourceAction.warningDialog.message=Data sources cannot be deleted when ingest is running.", "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?\nNote that the case will be closed and re-opened during the deletion.", - "DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source.\nPlease see the application log for details.", + "# {0} - exception message", "DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source:\n{0}\nPlease see the application log for details.", "DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case." }) @Override @@ -68,17 +66,18 @@ public final class DeleteDataSourceAction extends AbstractAction { MessageNotifyUtil.Message.warn(Bundle.DeleteDataSourceAction_warningDialog_message()); return; } - + if (MessageNotifyUtil.Message.confirm(Bundle.DeleteDataSourceAction_confirmationDialog_message())) { new SwingWorker() { @Override protected Void doInBackground() throws Exception { - caseMetadataFilePath = Case.getCurrentCase().getMetadata().getFilePath(); /* - * Note that the case is closed and re-opened by this case - * action. + * Save the case metadata file path so the case can be + * reopened if something goes wrong and the case ends up + * closed. */ + caseMetadataFilePath = Case.getCurrentCase().getMetadata().getFilePath(); Case.deleteDataSourceFromCurrentCase(dataSourceObjectID); return null; } @@ -89,7 +88,7 @@ public final class DeleteDataSourceAction extends AbstractAction { 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); + MessageNotifyUtil.Message.show(Bundle.DeleteDataSourceAction_exceptionMessage_dataSourceDeletionError(ex.getLocalizedMessage()), MessageNotifyUtil.MessageType.ERROR); if (!Case.isCaseOpen()) { try { Case.openAsCurrentCase(caseMetadataFilePath.toString()); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 9e4ec9eee8..f668893eb1 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -34,6 +34,7 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.DeleteDataSourceAction; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; diff --git a/Core/src/org/sleuthkit/autopsy/featureaccess/FeatureAccessUtils.java b/Core/src/org/sleuthkit/autopsy/featureaccess/FeatureAccessUtils.java index d646147c61..87b4fed4f7 100644 --- a/Core/src/org/sleuthkit/autopsy/featureaccess/FeatureAccessUtils.java +++ b/Core/src/org/sleuthkit/autopsy/featureaccess/FeatureAccessUtils.java @@ -28,6 +28,9 @@ import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber; /** * Check if access to various features is permitted for the current user and the * current case, if any. + * + * IMPORTANT: These utilities are not concerned with transitory restrictions on + * access to a feature, e.g., whether or not ingest is running. */ final public class FeatureAccessUtils { From 812ed6a4f7353ba3bab9ee30e915919087bcb032 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 11 Nov 2019 17:25:47 -0500 Subject: [PATCH 26/37] Data source deletion support changes --- .../sleuthkit/autopsy/casemodule/Case.java | 104 ++++++++++-------- .../autopsy/casemodule/services/Services.java | 4 +- .../autopsy/ingest/IngestServices.java | 1 - 3 files changed, 61 insertions(+), 48 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 717fbc97a2..e8b4fa7ec4 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -162,10 +162,9 @@ public class Case { private volatile ExecutorService caseActionExecutor; private CoordinationService.Lock caseLock; private SleuthkitCase caseDb; + private final SleuthkitEventListener sleuthkitEventListener; private CollaborationMonitor collaborationMonitor; private Services caseServices; - private boolean hasDataSources; - private final TSKCaseRepublisher tskEventForwarder; /* * Get a reference to the main window of the desktop application to use to @@ -409,10 +408,15 @@ public class Case { }; - private final class TSKCaseRepublisher { + /** + * An instance of this class is registered as a listener on the event bus + * associated with the case database so that selected SleuthKit layer + * application events can be published as Autopsy application events. + */ + private final class SleuthkitEventListener { @Subscribe - public void rebroadcastTimelineEventCreated(TimelineManager.TimelineEventAddedEvent event) { + public void publishTimelineEventAdded(TimelineManager.TimelineEventAddedEvent event) { eventPublisher.publish(new TimelineEventAddedEvent(event)); } @@ -421,10 +425,12 @@ public class Case { public void rebroadcastArtifactsPosted(Blackboard.ArtifactsPostedEvent event) { for (BlackboardArtifact.Type artifactType : event.getArtifactTypes()) { /* - * fireModuleDataEvent is deprecated so module writers don't use - * it (they should use Blackboard.postArtifact(s) instead), but - * we still need a way to rebroadcast the ArtifactsPostedEvent - * as a ModuleDataEvent. + * IngestServices.fireModuleDataEvent is deprecated to + * discourage ingest module writers from using it (they should + * use org.sleuthkit.datamodel.Blackboard.postArtifact(s) + * instead), but a way to publish + * Blackboard.ArtifactsPostedEvents from the SleuthKit layer as + * Autopsy ModuleDataEvents is still needed. */ IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent( event.getModuleName(), @@ -710,7 +716,7 @@ public class Case { eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), closedCase, null)); logger.log(Level.INFO, "Closing current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS currentCase = null; - closedCase.close(); + closedCase.doCloseCaseAction(); logger.log(Level.INFO, "Closed current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS } catch (CaseActionException ex) { logger.log(Level.SEVERE, String.format("Error closing current case %s (%s) in %s", closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()), ex); //NON-NLS @@ -760,10 +766,10 @@ public class Case { if (null == currentCase) { return; } - + /* * Close the current case to release the shared case lock. - */ + */ CaseMetadata caseMetadata = currentCase.getMetadata(); closeCurrentCase(); @@ -1421,16 +1427,14 @@ public class Case { /** * Gets the data sources for the case. * - * @return A list of data sources. + * @return A list of data sources, possibly empty. * * @throws org.sleuthkit.datamodel.TskCoreException if there is a problem * querying the case * database. */ public List getDataSources() throws TskCoreException { - List list = caseDb.getRootObjects(); - hasDataSources = (list.size() > 0); - return list; + return caseDb.getRootObjects(); } /** @@ -1471,12 +1475,11 @@ public class Case { * @return True or false. */ public boolean hasData() { - if (!hasDataSources) { - try { - hasDataSources = (getDataSources().size() > 0); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS - } + boolean hasDataSources = false; + try { + hasDataSources = (getDataSources().size() > 0); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS } return hasDataSources; } @@ -1764,25 +1767,22 @@ public class Case { */ private Case(CaseMetadata caseMetaData) { metadata = caseMetaData; - TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName())); - caseActionExecutor = Executors.newSingleThreadExecutor(threadFactory); - tskEventForwarder = new TSKCaseRepublisher(); + sleuthkitEventListener = new SleuthkitEventListener(); } /** - * Performs an open case action. If the case is a multi-user case, the - * action is done after acquiring a coordination service case lock. The case - * lock must eventually be released in the same thread in which it was - * acquired, as required by the coordination service. The open case action - * is therefore done in a task running in the single thread in the case - * action executor. + * Performs a case action that involves creating or opening a case. If the + * case is a multi-user case, the action is done after acquiring a + * coordination service case lock. This case lock must be released in the + * same thread in which it was acquired, as required by the coordination + * service. A single-threaded executor is therefore created to do the case + * opening action, and is saved for an eventual case closing action. * * IMPORTANT: If an open case action for a multi-user case is terminated * because an exception is thrown or the action is cancelled, the action is * responsible for releasing the case lock while still running in the case - * action executor thread. This is required because this method handles - * interrupts, cancellation exceptions, and execution exceptions by assuming - * the case will be closed and shutting down the case action executor. + * action executor's thread. This method assumes this has been done and + * performs an orderly shut down of the case action executor. * * @param progressIndicatorTitle A title for the progress indicator for the * case action. @@ -1839,11 +1839,13 @@ public class Case { * until explictly released and an exclusive case resources lock is * aquired and held for the duration of the action. */ + TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName())); + caseActionExecutor = Executors.newSingleThreadExecutor(threadFactory); Future future = caseActionExecutor.submit(() -> { if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { caseAction.execute(progressIndicator, additionalParams); } else { - progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources()); + progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources()); // RJCTODO: Make locking message acquireCaseLock(caseLockType); try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) { if (null == resourcesLock) { @@ -2308,6 +2310,7 @@ public class Case { } else { throw new CaseActionException(Bundle.Case_open_exception_multiUserCaseNotEnabled()); } + caseDb.registerForEvents(sleuthkitEventListener); } catch (TskUnsupportedSchemaVersionException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_unsupportedSchemaVersionMessage(ex.getLocalizedMessage()), ex); } catch (UserPreferencesException ex) { @@ -2328,7 +2331,6 @@ public class Case { private void openCaseLevelServices(ProgressIndicator progressIndicator) { progressIndicator.progress(Bundle.Case_progressMessage_openingCaseLevelServices()); this.caseServices = new Services(caseDb); - caseDb.registerForEvents(tskEventForwarder); } /** @@ -2358,7 +2360,9 @@ public class Case { * one starts by awaiting termination of the executor service. */ progressIndicator.progress(Bundle.Case_progressMessage_openingApplicationServiceResources()); - for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class)) { + + for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class + )) { /* * Create a progress indicator for the task and start the task. If * running with a GUI, the progress indicator will be a dialog box @@ -2472,14 +2476,20 @@ public class Case { } /** - * Closes the case. + * Performs a case action that involves closing a case opened by calling + * doOpenCaseAction. If the case is a multi-user case, the coordination + * service case lock acquired by the call to doOpenCaseAction is released. + * This case lock must be released in the same thread in which it was + * acquired, as required by the coordination service. The single-threaded + * executor saved during the case opening action is therefore used to do the + * case closing action. * - * @throws CaseActionException If there is a problem completing the - * operation. The exception will have a - * user-friendly message and may be a wrapper - * for a lower-level exception. + * @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 close() throws CaseActionException { + private void doCloseCaseAction() throws CaseActionException { /* * Set up either a GUI progress indicator without a Cancel button or a * logging progress indicator. @@ -2581,7 +2591,7 @@ public class Case { */ if (null != caseDb) { progressIndicator.progress(Bundle.Case_progressMessage_closingCaseDatabase()); - caseDb.unregisterForEvents(tskEventForwarder); + caseDb.unregisterForEvents(sleuthkitEventListener); caseDb.close(); } @@ -2605,7 +2615,8 @@ public class Case { * Each service gets its own independently cancellable task, and thus * its own task progress indicator. */ - for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class)) { + for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class + )) { ProgressIndicator progressIndicator; if (RuntimeProperties.runningWithGUI()) { progressIndicator = new ModalDialogProgressIndicator( @@ -2947,7 +2958,9 @@ public class Case { }) private static void deleteTextIndex(CaseMetadata metadata, ProgressIndicator progressIndicator) throws KeywordSearchServiceException { progressIndicator.progress(Bundle.Case_progressMessage_deletingTextIndex()); - for (KeywordSearchService searchService : Lookup.getDefault().lookupAll(KeywordSearchService.class)) { + + for (KeywordSearchService searchService : Lookup.getDefault().lookupAll(KeywordSearchService.class + )) { searchService.deleteTextIndex(metadata); } } @@ -3048,6 +3061,7 @@ public class Case { CaseNodeData.writeCaseNodeData(caseNodeData); } catch (CaseNodeDataException ex) { logger.log(Level.SEVERE, String.format("Error updating deleted item flag %s for %s (%s) in %s", flag.name(), caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex); + } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/Services.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/Services.java index 81bc6a643c..8764657507 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/Services.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/Services.java @@ -103,8 +103,8 @@ public class Services implements Closeable { /** * Closes the services for the current case. * - * @throws IOException if there is a problem closing the services. - * @deprecated Do not use. + * @throws IOException if there is a problem closing the services.sleuthkitCaseL + * @deprecated Do not use. */ @Deprecated @Override diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestServices.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestServices.java index 3117ef2f98..7bad9ebf98 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestServices.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestServices.java @@ -110,7 +110,6 @@ public final class IngestServices { @Deprecated public void fireModuleDataEvent(ModuleDataEvent moduleDataEvent) { IngestManager.getInstance().fireIngestModuleDataEvent(moduleDataEvent); - } /** From b324e18d5b29a1669b7054aad7b853a30cfaa94a Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 12 Nov 2019 11:47:17 -0500 Subject: [PATCH 27/37] Data source deletion support changes --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index e8b4fa7ec4..e3eda0664c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -2666,8 +2666,8 @@ public class Case { try { CoordinationService coordinationService = CoordinationService.getInstance(); caseLock = lockType == CaseLockType.SHARED - ? coordinationService.tryGetSharedLock(CategoryNode.CASES, caseDir, SHARED_CASE_LOCK_TIMEOUT_SECONDS, TimeUnit.MINUTES) - : coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseDir, EXCLUSIVE_CASE_LOCK_TIMEOUT_MINS, TimeUnit.SECONDS); + ? coordinationService.tryGetSharedLock(CategoryNode.CASES, caseDir, SHARED_CASE_LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS) + : coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseDir, EXCLUSIVE_CASE_LOCK_TIMEOUT_MINS, TimeUnit.MINUTES); if (caseLock == null) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock()); } From d298d1766c8a26c2c8732f80f025687b5321cf7a Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 12 Nov 2019 15:39:48 -0500 Subject: [PATCH 28/37] Data source deletion support changes --- .../sleuthkit/autopsy/casemodule/Case.java | 21 ++++++------------- .../casemodule/DeleteDataSourceAction.java | 2 +- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index e3eda0664c..ecacc6a1c6 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -1802,7 +1802,6 @@ public class Case { @Messages({ "Case.progressIndicatorCancelButton.label=Cancel", "Case.progressMessage.preparing=Preparing...", - "Case.progressMessage.preparingToOpenCaseResources=Preparing to open case resources.
This may take time if another user is upgrading the case.", "Case.progressMessage.cancelling=Cancelling...", "Case.exceptionMessage.cancelled=Cancelled.", "# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}" @@ -1845,7 +1844,6 @@ public class Case { if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { caseAction.execute(progressIndicator, additionalParams); } else { - progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources()); // RJCTODO: Make locking message acquireCaseLock(caseLockType); try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) { if (null == resourcesLock) { @@ -1888,7 +1886,7 @@ public class Case { /* * The case action has thrown an exception. */ - ThreadUtils.shutDownTaskExecutor(caseActionExecutor); // RJCTODO: Log error ion the thread where a stack trace can be recorded + ThreadUtils.shutDownTaskExecutor(caseActionExecutor); throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex); } finally { progressIndicator.finish(); @@ -1934,14 +1932,11 @@ public class Case { } catch (CaseActionException ex) { /* - * Cancellation or failure. Log the exception now, while running in - * the case action executor with the stack trace available, then - * clean up by calling the close method. The sleep is a little hack + * Cancellation or failure. 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. */ - logger.log(Level.WARNING, "Failed to open the case", ex); // RJCTODO try { Thread.sleep(1); } catch (InterruptedException discarded) { @@ -1986,14 +1981,11 @@ public class Case { } catch (CaseActionException ex) { /* - * Cancellation or failure. Log the exception now, while running in - * the case action executor with the stack trace available, then - * clean up by calling the close method. The sleep is a little hack + * Cancellation or failure. 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. */ - logger.log(Level.WARNING, "Failed to open the case", ex); // RJCTODO try { Thread.sleep(1); } catch (InterruptedException discarded) { @@ -2043,12 +2035,11 @@ public class Case { throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSource(ex.getMessage()), ex); } eventPublisher.publish(new DataSourceDeletedEvent(dataSourceObjectID)); - } catch (CaseActionException ex) { + return null; + } finally { close(progressIndicator); - throw ex; + releaseCaseLock(); } - close(progressIndicator); - return null; } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index cb4c02d51a..0b88ef22a9 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -56,7 +56,7 @@ public final class DeleteDataSourceAction extends AbstractAction { @NbBundle.Messages({ "DeleteDataSourceAction.warningDialog.message=Data sources cannot be deleted when ingest is running.", - "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?\nNote that the case will be closed and re-opened during the deletion.", + "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to remove the selected data source from the case?\nNote that the case will be closed and re-opened during the deletion.", "# {0} - exception message", "DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source:\n{0}\nPlease see the application log for details.", "DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case." }) From ad424a6f3311e57b350f02dd581353ddafdd49ce Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 12 Nov 2019 16:21:25 -0500 Subject: [PATCH 29/37] Integrate and update data src deletion --- .../autopsy/casemodule/Bundle.properties-MERGED | 3 +-- .../org/sleuthkit/autopsy/casemodule/Case.java | 15 ++++++++------- .../casemodule/DeleteDataSourceAction.java | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 2cd4dd414d..b12a09f930 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -79,7 +79,6 @@ Case.progressMessage.openingApplicationServiceResources=Opening application serv Case.progressMessage.openingCaseDatabase=Opening case database... Case.progressMessage.openingCaseLevelServices=Opening case-level services... Case.progressMessage.preparing=Preparing... -Case.progressMessage.preparingToOpenCaseResources=Preparing to open case resources.
This may take time if another user is upgrading the case. Case.progressMessage.removingCaseFromRecentCases=Removing case from Recent Cases menu... Case.progressMessage.savingCaseMetadata=Saving case metadata to file... Case.progressMessage.settingUpNetworkCommunications=Setting up network communications... @@ -123,7 +122,7 @@ CTL_CaseDetailsAction=Case Details CTL_CaseDeleteAction=Delete Case CTL_CaseOpenAction=Open 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?\nNote that the case will be closed and re-opened during the deletion. +DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to remove the selected data source from the case?\nNote that the case will be closed and re-opened during the deletion. DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case. # {0} - exception message DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source:\n{0}\nPlease see the application log for details. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index ecacc6a1c6..38bdb0ca09 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -416,13 +416,13 @@ public class Case { private final class SleuthkitEventListener { @Subscribe - public void publishTimelineEventAdded(TimelineManager.TimelineEventAddedEvent event) { + public void publishTimelineEventAddedEvent(TimelineManager.TimelineEventAddedEvent event) { eventPublisher.publish(new TimelineEventAddedEvent(event)); } @SuppressWarnings("deprecation") @Subscribe - public void rebroadcastArtifactsPosted(Blackboard.ArtifactsPostedEvent event) { + public void publishArtifactsPostedEvent(Blackboard.ArtifactsPostedEvent event) { for (BlackboardArtifact.Type artifactType : event.getArtifactTypes()) { /* * IngestServices.fireModuleDataEvent is deprecated to @@ -759,7 +759,7 @@ public class Case { * exception. */ @Messages({ - "Case.progressIndicatorTitle.deletingDataSource=Deleting Data Source" + "Case.progressIndicatorTitle.deletingDataSource=Removing Data Source" }) public static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException { synchronized (caseActionSerializationLock) { @@ -2010,9 +2010,10 @@ public class Case { * lower-level exception. */ @Messages({ - "Case.progressMessage.deletingDataSource=Deleting the data source from the case...", + "Case.progressMessage.deletingDataSource=Removing the data source from the case...", "Case.exceptionMessage.dataSourceNotFound=The data source was not found.", - "# {0} - exception message", "Case.exceptionMessage.errorDeletingDataSource=An error occurred while deleting the data source:\n{0}." + "Case.exceptionMessage.errorDeletingDataSourceFromCaseDb=An error occurred while deleting the data source from the case database.", + "Case.exceptionMessage.errorDeletingDataSourceFromTextIndex=An error occurred while deleting the data source from the text index.", }) Void deleteDataSource(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { assert (additionalParams instanceof Long); @@ -2027,12 +2028,12 @@ public class Case { } SleuthkitCaseAdmin.deleteDataSource(this.caseDb, dataSourceObjectID); } catch (TskDataException | TskCoreException ex) { - throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSource(ex.getMessage()), ex); + throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSourceFromCaseDb(), ex); } try { this.caseServices.getKeywordSearchService().deleteDataSource(dataSourceObjectID); } catch (KeywordSearchServiceException ex) { - throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSource(ex.getMessage()), ex); + throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSourceFromTextIndex(), ex); } eventPublisher.publish(new DataSourceDeletedEvent(dataSourceObjectID)); return null; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index 0b88ef22a9..69c1d8a06d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -31,7 +31,7 @@ import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils; import org.sleuthkit.autopsy.ingest.IngestManager; /** - * An Action that allows a user to delete a data source from the current case. + * An Action that allows a user to remove a data source from the current case. */ public final class DeleteDataSourceAction extends AbstractAction { @@ -46,7 +46,7 @@ public final class DeleteDataSourceAction extends AbstractAction { * @param dataSourceObjectID The object ID of the data source to be deleted. */ @NbBundle.Messages({ - "DeleteDataSourceAction.name.text=Delete Data Source" + "DeleteDataSourceAction.name.text=Remove Data Source" }) public DeleteDataSourceAction(Long dataSourceObjectID) { super(Bundle.DeleteDataSourceAction_name_text()); @@ -55,9 +55,9 @@ public final class DeleteDataSourceAction extends AbstractAction { } @NbBundle.Messages({ - "DeleteDataSourceAction.warningDialog.message=Data sources cannot be deleted when ingest is running.", - "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to remove the selected data source from the case?\nNote that the case will be closed and re-opened during the deletion.", - "# {0} - exception message", "DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source:\n{0}\nPlease see the application log for details.", + "DeleteDataSourceAction.warningDialog.message=Data sources cannot be removed from a case when ingest is running.", + "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to remove the selected data source from the case?\nNote that the case will be closed and re-opened during the removal.", + "# {0} - exception message", "DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while removing the data source:\n{0}\nPlease see the application log for details.", "DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case." }) @Override From 0716587b95db310d62f885760fae6c5f33ecf123 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 12 Nov 2019 16:23:44 -0500 Subject: [PATCH 30/37] Data source deletion support --- .../casemodule/Bundle.properties-MERGED | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index b12a09f930..3881718bd6 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -37,8 +37,8 @@ Case.exceptionMessage.dataSourceNotFound=The data source was not found. Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled. Case.exceptionMessage.emptyCaseDir=Must specify a case directory path. 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.errorDeletingDataSourceFromCaseDb=An error occurred while deleting the data source from the case database. +Case.exceptionMessage.errorDeletingDataSourceFromTextIndex=An error occurred while deleting the data source from the text index. Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details. # {0} - exception message Case.exceptionMessage.execExceptionWrapperMessage={0} @@ -58,7 +58,7 @@ Case.progressIndicatorCancelButton.label=Cancel Case.progressIndicatorTitle.closingCase=Closing Case Case.progressIndicatorTitle.creatingCase=Creating Case Case.progressIndicatorTitle.deletingCase=Deleting Case -Case.progressIndicatorTitle.deletingDataSource=Deleting Data Source +Case.progressIndicatorTitle.deletingDataSource=Removing Data Source Case.progressIndicatorTitle.openingCase=Opening Case Case.progressMessage.cancelling=Cancelling... Case.progressMessage.clearingTempDirectory=Clearing case temp directory... @@ -71,7 +71,7 @@ Case.progressMessage.creatingCaseNodeData=Creating coordination service node dat Case.progressMessage.deletingCaseDatabase=Deleting case database... Case.progressMessage.deletingCaseDirCoordSvcNode=Deleting case directory coordination service node... Case.progressMessage.deletingCaseDirectory=Deleting case directory... -Case.progressMessage.deletingDataSource=Deleting the data source from the case... +Case.progressMessage.deletingDataSource=Removing the data source from the case... Case.progressMessage.deletingResourcesCoordSvcNode=Deleting case resources coordination service node... Case.progressMessage.deletingTextIndex=Deleting text index... Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case... @@ -122,12 +122,12 @@ CTL_CaseDetailsAction=Case Details CTL_CaseDeleteAction=Delete Case CTL_CaseOpenAction=Open Case CTL_UnpackagePortableCaseAction=Unpack and Open Portable Case -DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to remove the selected data source from the case?\nNote that the case will be closed and re-opened during the deletion. +DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to remove the selected data source from the case?\nNote that the case will be closed and re-opened during the removal. DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case. # {0} - exception message -DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source:\n{0}\nPlease see the application log for details. -DeleteDataSourceAction.name.text=Delete Data Source -DeleteDataSourceAction.warningDialog.message=Data sources cannot be deleted when ingest is running. +DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while removing the data source:\n{0}\nPlease see the application log for details. +DeleteDataSourceAction.name.text=Remove Data Source +DeleteDataSourceAction.warningDialog.message=Data sources cannot be removed from a case when ingest is running. EditOptionalCasePropertiesPanel.cancelButton.text=Cancel EditOptionalCasePropertiesPanel.saveButton.text=Save GeneralFilter.encaseImageDesc.text=Encase Images (*.e01) @@ -240,15 +240,10 @@ AddImageWizardIngestConfigPanel.dsProcDone.errs.text=*Errors encountered in addi AddImageWizardIngestConfigVisual.getName.text=Configure Ingest Modules AddImageWizardIterator.stepXofN=Step {0} of {1} AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1} -Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open\! +Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open! Case.open.msgDlg.updated.msg=Updated case database schema.\nA backup copy of the database with the following path has been made:\n {0} Case.open.msgDlg.updated.title=Case Database Schema Update -Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \n\ -this case are missing. Would you like to search for them now?\n\ -Previously, the image was located at:\n\ -{0}\n\ -Please note that you will still be able to browse directories and generate reports\n\ -if you choose No, but you will not be able to view file content or run the ingest process. +Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \nthis case are missing. Would you like to search for them now?\nPreviously, the image was located at:\n{0}\nPlease note that you will still be able to browse directories and generate reports\nif you choose No, but you will not be able to view file content or run the ingest process. Case.checkImgExist.confDlg.doesntExist.title=Missing Image Case.addImg.exception.msg=Error adding image to the case Case.updateCaseName.exception.msg=Error while trying to update the case name. @@ -267,12 +262,9 @@ Case.GetCaseTypeGivenPath.Failure=Unable to get case type Case.metaDataFileCorrupt.exception.msg=The case metadata file (.aut) is corrupted. Case.deleteReports.deleteFromDiskException.log.msg=Unable to delete the report from the disk. Case.deleteReports.deleteFromDiskException.msg=Unable to delete the report {0} from the disk.\nYou may manually delete it from {1} -CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \n\ - Case Name: {0}\n\ - Case Directory: {1} +CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \nCase Name: {0}\nCase Directory: {1} CaseDeleteAction.closeConfMsg.title=Warning: Closing the Current Case -CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\n\ -Close the folder and file and try again or you can delete the case manually. +CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\nClose the folder and file and try again or you can delete the case manually. CaseDeleteAction.msgDlg.fileInUse.title=Error: Folder In Use CaseDeleteAction.msgDlg.caseDelete.msg=Case {0} has been deleted. CaseOpenAction.autFilter.title={0} Case File ( {1}) @@ -304,8 +296,7 @@ NewCaseWizardAction.databaseProblem1.text=Cannot open database. Cancelling case NewCaseWizardAction.databaseProblem2.text=Error NewCaseWizardPanel1.validate.errMsg.invalidSymbols=The Case Name cannot contain any of the following symbols: \\ / : * ? " < > | NewCaseWizardPanel1.validate.errMsg.dirExists=Case directory ''{0}'' already exists. -NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\n\ - Do you want to create that directory? +NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\nDo you want to create that directory? NewCaseWizardPanel1.validate.confMsg.createDir.title=Create directory NewCaseWizardPanel1.validate.errMsg.cantCreateParDir.msg=Error: Could not create case parent directory {0} NewCaseWizardPanel1.validate.errMsg.prevCreateBaseDir.msg=Prevented from creating base directory {0} @@ -355,15 +346,15 @@ UnpackageWorker.doInBackground.errorFinding7zip=Could not locate 7-Zip executabl UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases UpdateRecentCases.menuItem.empty=-Empty- AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel -NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive -NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on \"C:\" drive. Case folder is created on the target system +NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on "C:" drive +NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on "C:" drive. Case folder is created on the target system NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning: Path to case folder is on the target system. Create case folder in mounted drive. CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1} MissingImageDialog.lbWarning.text= MissingImageDialog.lbWarning.toolTipText= NewCaseVisualPanel1.caseParentDirWarningLabel.text= -NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user +NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user\t\t NewCaseVisualPanel1.singleUserCaseRadioButton.text=Single-user NewCaseVisualPanel1.caseTypeLabel.text=Case Type: SingleUserCaseConverter.BadDatabaseFileName=Database file does not exist! From f9bacacd173702b21e36b5b99e339f5b21b162a1 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 12 Nov 2019 16:31:12 -0500 Subject: [PATCH 31/37] Integrate and update data src deletion --- .../casemodule/Bundle.properties-MERGED | 32 ++++++++++++------- .../sleuthkit/autopsy/casemodule/Case.java | 4 +-- .../casemodule/DeleteDataSourceAction.java | 4 +-- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 3881718bd6..e8e65080af 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -37,8 +37,8 @@ Case.exceptionMessage.dataSourceNotFound=The data source was not found. Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled. Case.exceptionMessage.emptyCaseDir=Must specify a case directory path. Case.exceptionMessage.emptyCaseName=Must specify a case name. -Case.exceptionMessage.errorDeletingDataSourceFromCaseDb=An error occurred while deleting the data source from the case database. -Case.exceptionMessage.errorDeletingDataSourceFromTextIndex=An error occurred while deleting the data source from the text index. +Case.exceptionMessage.errorDeletingDataSourceFromCaseDb=An error occurred while removing the data source from the case database. +Case.exceptionMessage.errorDeletingDataSourceFromTextIndex=An error occurred while removing the data source from the text index. Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details. # {0} - exception message Case.exceptionMessage.execExceptionWrapperMessage={0} @@ -123,7 +123,8 @@ CTL_CaseDeleteAction=Delete Case CTL_CaseOpenAction=Open Case CTL_UnpackagePortableCaseAction=Unpack and Open Portable Case DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to remove the selected data source from the case?\nNote that the case will be closed and re-opened during the removal. -DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case. +# {0} - exception message +DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case:\n{0}\nPlease see the application log for details. # {0} - exception message DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while removing the data source:\n{0}\nPlease see the application log for details. DeleteDataSourceAction.name.text=Remove Data Source @@ -240,10 +241,15 @@ AddImageWizardIngestConfigPanel.dsProcDone.errs.text=*Errors encountered in addi AddImageWizardIngestConfigVisual.getName.text=Configure Ingest Modules AddImageWizardIterator.stepXofN=Step {0} of {1} AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1} -Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open! +Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open\! Case.open.msgDlg.updated.msg=Updated case database schema.\nA backup copy of the database with the following path has been made:\n {0} Case.open.msgDlg.updated.title=Case Database Schema Update -Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \nthis case are missing. Would you like to search for them now?\nPreviously, the image was located at:\n{0}\nPlease note that you will still be able to browse directories and generate reports\nif you choose No, but you will not be able to view file content or run the ingest process. +Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \n\ +this case are missing. Would you like to search for them now?\n\ +Previously, the image was located at:\n\ +{0}\n\ +Please note that you will still be able to browse directories and generate reports\n\ +if you choose No, but you will not be able to view file content or run the ingest process. Case.checkImgExist.confDlg.doesntExist.title=Missing Image Case.addImg.exception.msg=Error adding image to the case Case.updateCaseName.exception.msg=Error while trying to update the case name. @@ -262,9 +268,12 @@ Case.GetCaseTypeGivenPath.Failure=Unable to get case type Case.metaDataFileCorrupt.exception.msg=The case metadata file (.aut) is corrupted. Case.deleteReports.deleteFromDiskException.log.msg=Unable to delete the report from the disk. Case.deleteReports.deleteFromDiskException.msg=Unable to delete the report {0} from the disk.\nYou may manually delete it from {1} -CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \nCase Name: {0}\nCase Directory: {1} +CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \n\ + Case Name: {0}\n\ + Case Directory: {1} CaseDeleteAction.closeConfMsg.title=Warning: Closing the Current Case -CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\nClose the folder and file and try again or you can delete the case manually. +CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\n\ +Close the folder and file and try again or you can delete the case manually. CaseDeleteAction.msgDlg.fileInUse.title=Error: Folder In Use CaseDeleteAction.msgDlg.caseDelete.msg=Case {0} has been deleted. CaseOpenAction.autFilter.title={0} Case File ( {1}) @@ -296,7 +305,8 @@ NewCaseWizardAction.databaseProblem1.text=Cannot open database. Cancelling case NewCaseWizardAction.databaseProblem2.text=Error NewCaseWizardPanel1.validate.errMsg.invalidSymbols=The Case Name cannot contain any of the following symbols: \\ / : * ? " < > | NewCaseWizardPanel1.validate.errMsg.dirExists=Case directory ''{0}'' already exists. -NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\nDo you want to create that directory? +NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\n\ + Do you want to create that directory? NewCaseWizardPanel1.validate.confMsg.createDir.title=Create directory NewCaseWizardPanel1.validate.errMsg.cantCreateParDir.msg=Error: Could not create case parent directory {0} NewCaseWizardPanel1.validate.errMsg.prevCreateBaseDir.msg=Prevented from creating base directory {0} @@ -346,15 +356,15 @@ UnpackageWorker.doInBackground.errorFinding7zip=Could not locate 7-Zip executabl UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases UpdateRecentCases.menuItem.empty=-Empty- AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel -NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on "C:" drive -NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on "C:" drive. Case folder is created on the target system +NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive +NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on \"C:\" drive. Case folder is created on the target system NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning: Path to case folder is on the target system. Create case folder in mounted drive. CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1} MissingImageDialog.lbWarning.text= MissingImageDialog.lbWarning.toolTipText= NewCaseVisualPanel1.caseParentDirWarningLabel.text= -NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user\t\t +NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user NewCaseVisualPanel1.singleUserCaseRadioButton.text=Single-user NewCaseVisualPanel1.caseTypeLabel.text=Case Type: SingleUserCaseConverter.BadDatabaseFileName=Database file does not exist! diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 38bdb0ca09..9ff55cdb3b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -2012,8 +2012,8 @@ public class Case { @Messages({ "Case.progressMessage.deletingDataSource=Removing the data source from the case...", "Case.exceptionMessage.dataSourceNotFound=The data source was not found.", - "Case.exceptionMessage.errorDeletingDataSourceFromCaseDb=An error occurred while deleting the data source from the case database.", - "Case.exceptionMessage.errorDeletingDataSourceFromTextIndex=An error occurred while deleting the data source from the text index.", + "Case.exceptionMessage.errorDeletingDataSourceFromCaseDb=An error occurred while removing the data source from the case database.", + "Case.exceptionMessage.errorDeletingDataSourceFromTextIndex=An error occurred while removing the data source from the text index.", }) Void deleteDataSource(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { assert (additionalParams instanceof Long); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index 69c1d8a06d..5876b19a1c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -58,7 +58,7 @@ public final class DeleteDataSourceAction extends AbstractAction { "DeleteDataSourceAction.warningDialog.message=Data sources cannot be removed from a case when ingest is running.", "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to remove the selected data source from the case?\nNote that the case will be closed and re-opened during the removal.", "# {0} - exception message", "DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while removing the data source:\n{0}\nPlease see the application log for details.", - "DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case." + "# {0} - exception message", "DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case:\n{0}\nPlease see the application log for details." }) @Override public void actionPerformed(ActionEvent event) { @@ -94,7 +94,7 @@ public final class DeleteDataSourceAction extends AbstractAction { 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); + MessageNotifyUtil.Message.show(Bundle.DeleteDataSourceAction_exceptionMessage_couldNotReopenCase(ex.getLocalizedMessage()), MessageNotifyUtil.MessageType.ERROR); StartupWindowProvider.getInstance().open(); } } From fc7f4f1dbe4fe31fb0d129b6ea3f85b8555aacc2 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 12 Nov 2019 16:33:38 -0500 Subject: [PATCH 32/37] Integrate and update data src deletion --- .../sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index 5876b19a1c..e8c46ea002 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -41,9 +41,10 @@ public final class DeleteDataSourceAction extends AbstractAction { private Path caseMetadataFilePath; /** - * Constructs an Action that allows a user to delete a data source. + * Constructs an Action that allows a user to remove a data source from a + * case. * - * @param dataSourceObjectID The object ID of the data source to be deleted. + * @param dataSourceObjectID The object ID of the data source to be removed. */ @NbBundle.Messages({ "DeleteDataSourceAction.name.text=Remove Data Source" From de6872b2fb71cd13677316ad58935571d734f66d Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 12 Nov 2019 16:35:25 -0500 Subject: [PATCH 33/37] Integrate and update data src deletion --- .../sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index e8c46ea002..f4b03b1b41 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -56,7 +56,7 @@ public final class DeleteDataSourceAction extends AbstractAction { } @NbBundle.Messages({ - "DeleteDataSourceAction.warningDialog.message=Data sources cannot be removed from a case when ingest is running.", + "DeleteDataSourceAction.ingestRunningWarningDialog.message=Data sources cannot be removed from a case when ingest is running.", "DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to remove the selected data source from the case?\nNote that the case will be closed and re-opened during the removal.", "# {0} - exception message", "DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while removing the data source:\n{0}\nPlease see the application log for details.", "# {0} - exception message", "DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case:\n{0}\nPlease see the application log for details." @@ -64,7 +64,7 @@ public final class DeleteDataSourceAction extends AbstractAction { @Override public void actionPerformed(ActionEvent event) { if (IngestManager.getInstance().isIngestRunning()) { - MessageNotifyUtil.Message.warn(Bundle.DeleteDataSourceAction_warningDialog_message()); + MessageNotifyUtil.Message.warn(Bundle.DeleteDataSourceAction_ingestRunningWarningDialog_message()); return; } From 360c0a293d899e8a2409f25fa09db45c38ec10bc Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 12 Nov 2019 17:12:39 -0500 Subject: [PATCH 34/37] Integrate and update data src deletion --- .../casemodule/Bundle.properties-MERGED | 5 +- .../sleuthkit/autopsy/casemodule/Case.java | 36 ++++--- .../casemodule/DeleteDataSourceAction.java | 93 ++++++++++++------- .../netbeans/core/startup/Bundle.properties | 2 +- .../core/windows/view/ui/Bundle.properties | 2 +- 5 files changed, 88 insertions(+), 50 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index e8e65080af..9605ec34ad 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -2,7 +2,6 @@ AddImageWizardIngestConfigPanel.name.text=Configure Ingest Modules AddImageWizardSelectDspVisual.multiUserWarning.text=This type of Data Source Processor is not available in multi-user mode # {0} - exception message Case.closeException.couldNotCloseCase=Error closing case: {0} -Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources Case.deleteCaseConfirmationDialog.message=Are you sure you want to close and delete the current case? Case.deleteCaseConfirmationDialog.title=Delete Current Case? @@ -53,6 +52,8 @@ Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}. Case.exceptionMessage.metadataUpdateError=Failed to update case metadata # {0} - exception message Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}. +Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a shared lock on the case +Case.lockingException.couldNotAcquireSharedLock=Failed to get an exclusive lock on the case 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.progressIndicatorTitle.closingCase=Closing Case @@ -127,8 +128,8 @@ DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to remov DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to re-open the case:\n{0}\nPlease see the application log for details. # {0} - exception message DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while removing the data source:\n{0}\nPlease see the application log for details. +DeleteDataSourceAction.ingestRunningWarningDialog.message=Data sources cannot be removed from a case when ingest is running. DeleteDataSourceAction.name.text=Remove Data Source -DeleteDataSourceAction.warningDialog.message=Data sources cannot be removed from a case when ingest is running. EditOptionalCasePropertiesPanel.cancelButton.text=Cancel EditOptionalCasePropertiesPanel.saveButton.text=Save GeneralFilter.encaseImageDesc.text=Encase Images (*.e01) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 9ff55cdb3b..a2f5d1d966 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -1932,10 +1932,10 @@ public class Case { } catch (CaseActionException ex) { /* - * Cancellation or failure. 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. + * Cancellation or failure. 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); @@ -1981,10 +1981,10 @@ public class Case { } catch (CaseActionException ex) { /* - * Cancellation or failure. 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. + * Cancellation or failure. 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); @@ -2013,8 +2013,7 @@ public class Case { "Case.progressMessage.deletingDataSource=Removing the data source from the case...", "Case.exceptionMessage.dataSourceNotFound=The data source was not found.", "Case.exceptionMessage.errorDeletingDataSourceFromCaseDb=An error occurred while removing the data source from the case database.", - "Case.exceptionMessage.errorDeletingDataSourceFromTextIndex=An error occurred while removing the data source from the text index.", - }) + "Case.exceptionMessage.errorDeletingDataSourceFromTextIndex=An error occurred while removing the data source from the text index.",}) Void deleteDataSource(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException { assert (additionalParams instanceof Long); open(progressIndicator, null); @@ -2652,7 +2651,10 @@ public class Case { * * @throws CaseActionException If the lock cannot be acquired. */ - @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory"}) + @Messages({ + "Case.lockingException.couldNotAcquireSharedLock=Failed to get an exclusive lock on the case", + "Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a shared lock on the case" + }) private void acquireCaseLock(CaseLockType lockType) throws CaseActionException { String caseDir = metadata.getCaseDirectory(); try { @@ -2661,10 +2663,18 @@ public class Case { ? coordinationService.tryGetSharedLock(CategoryNode.CASES, caseDir, SHARED_CASE_LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS) : coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseDir, EXCLUSIVE_CASE_LOCK_TIMEOUT_MINS, TimeUnit.MINUTES); if (caseLock == null) { - throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock()); + if (lockType == CaseLockType.SHARED) { + throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireSharedLock()); + } else { + throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireExclusiveLock()); + } } } catch (InterruptedException | CoordinationServiceException ex) { - throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock(), ex); + if (lockType == CaseLockType.SHARED) { + throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireSharedLock(), ex); + } else { + throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireExclusiveLock(), ex); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java index f4b03b1b41..e603b4045f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/DeleteDataSourceAction.java @@ -69,42 +69,69 @@ public final class DeleteDataSourceAction extends AbstractAction { } if (MessageNotifyUtil.Message.confirm(Bundle.DeleteDataSourceAction_confirmationDialog_message())) { - new SwingWorker() { - - @Override - protected Void doInBackground() throws Exception { - /* - * Save the case metadata file path so the case can be - * reopened if something goes wrong and the case ends up - * closed. - */ - caseMetadataFilePath = Case.getCurrentCase().getMetadata().getFilePath(); - 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(ex.getLocalizedMessage()), 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(ex.getLocalizedMessage()), MessageNotifyUtil.MessageType.ERROR); - StartupWindowProvider.getInstance().open(); - } - } - } - } - }.execute(); + new DataSourceDeletionWorker().execute(); } } + /** + * A SwingWorker to do the data source deletion. + */ + private class DataSourceDeletionWorker extends SwingWorker { + + @Override + protected Void doInBackground() throws Exception { + /* + * Save the case metadata file path so the case can be reopened if + * something goes wrong and the case ends up closed. + */ + caseMetadataFilePath = Case.getCurrentCase().getMetadata().getFilePath(); + 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(ex.getLocalizedMessage()), MessageNotifyUtil.MessageType.ERROR); + if (!Case.isCaseOpen()) { + new CaseReopeningWorker().execute(); + } + } + } + + } + + /** + * A SwingWorker to attempt to re-open the case after a data source deletion + * exception. + */ + private class CaseReopeningWorker extends SwingWorker { + + @Override + protected Void doInBackground() throws Exception { + Case.openAsCurrentCase(caseMetadataFilePath.toString()); + return null; + } + + @Override + protected void done() { + try { + get(); + } catch (InterruptedException ex) { + logger.log(Level.WARNING, String.format("Interrupted reopening case after error deleting data source (obj_id=%d)", dataSourceObjectID), ex); + + } catch (ExecutionException ex) { + logger.log(Level.SEVERE, String.format("Error reopening case after error deleting data source (obj_id=%d)", dataSourceObjectID), ex); + MessageNotifyUtil.Message.show(Bundle.DeleteDataSourceAction_exceptionMessage_dataSourceDeletionError(ex.getCause().getLocalizedMessage()), MessageNotifyUtil.MessageType.ERROR); + StartupWindowProvider.getInstance().open(); + } + } + + } + @Override public DeleteDataSourceAction clone() throws CloneNotSupportedException { DeleteDataSourceAction clonedObject = ((DeleteDataSourceAction) super.clone()); diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index cd78d5c461..cd55f492ea 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Fri, 08 Nov 2019 16:32:05 -0500 +#Tue, 12 Nov 2019 16:58:46 -0500 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 721ab292cd..06565e185c 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Fri, 08 Nov 2019 16:32:05 -0500 +#Tue, 12 Nov 2019 16:58:46 -0500 CTL_MainWindow_Title=Autopsy 4.13.0 CTL_MainWindow_Title_No_Project=Autopsy 4.13.0 From 5953836533df629fccadf846fd9309780628fb9b Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 12 Nov 2019 17:18:03 -0500 Subject: [PATCH 35/37] Integrate and update data src deletion --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 2 +- .../src/org/sleuthkit/autopsy/casemodule/services/Services.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index a2f5d1d966..bde9bb7354 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -1039,7 +1039,7 @@ public class Case { @Messages({ "Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources" }) - private static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseDir) throws CaseActionException { + private static CoordinationService.Lock acquireCaseResourcesLock(String caseDir) throws CaseActionException { try { Path caseDirPath = Paths.get(caseDir); String resourcesNodeName = CoordinationServiceUtils.getCaseResourcesNodePath(caseDirPath); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/Services.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/Services.java index 8764657507..8cbe25e345 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/Services.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/Services.java @@ -103,7 +103,7 @@ public class Services implements Closeable { /** * Closes the services for the current case. * - * @throws IOException if there is a problem closing the services.sleuthkitCaseL + * @throws IOException if there is a problem closing the services. * @deprecated Do not use. */ @Deprecated From e14d49aba63bcb3956bb2640a166424246f94281 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 12 Nov 2019 17:30:39 -0500 Subject: [PATCH 36/37] Integrate and update data src deletion --- .../casemodule/Bundle.properties-MERGED | 25 ++++++------------- .../sleuthkit/autopsy/casemodule/Case.java | 15 ++++++----- .../Bundle.properties-MERGED | 5 +--- .../autopsy/core/Bundle.properties-MERGED | 8 +----- .../corecomponents/Bundle.properties-MERGED | 6 ++--- .../coreutils/Bundle.properties-MERGED | 4 +-- .../datamodel/Bundle.properties-MERGED | 9 +++---- .../filesearch/Bundle.properties-MERGED | 4 +-- .../autopsy/ingest/Bundle.properties-MERGED | 2 +- .../Bundle.properties-MERGED | 7 +----- .../modules/exif/Bundle.properties-MERGED | 4 +-- .../fileextmismatch/Bundle.properties-MERGED | 18 ++++++------- .../hashdatabase/Bundle.properties-MERGED | 10 ++------ .../interestingitems/Bundle.properties-MERGED | 4 +-- .../photoreccarver/Bundle.properties-MERGED | 2 +- .../modules/html/Bundle.properties-MERGED | 6 ++--- .../netbeans/core/startup/Bundle.properties | 2 +- .../core/windows/view/ui/Bundle.properties | 2 +- 18 files changed, 49 insertions(+), 84 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 9605ec34ad..75145f8a17 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -242,15 +242,10 @@ AddImageWizardIngestConfigPanel.dsProcDone.errs.text=*Errors encountered in addi AddImageWizardIngestConfigVisual.getName.text=Configure Ingest Modules AddImageWizardIterator.stepXofN=Step {0} of {1} AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1} -Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open\! +Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open! Case.open.msgDlg.updated.msg=Updated case database schema.\nA backup copy of the database with the following path has been made:\n {0} Case.open.msgDlg.updated.title=Case Database Schema Update -Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \n\ -this case are missing. Would you like to search for them now?\n\ -Previously, the image was located at:\n\ -{0}\n\ -Please note that you will still be able to browse directories and generate reports\n\ -if you choose No, but you will not be able to view file content or run the ingest process. +Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \nthis case are missing. Would you like to search for them now?\nPreviously, the image was located at:\n{0}\nPlease note that you will still be able to browse directories and generate reports\nif you choose No, but you will not be able to view file content or run the ingest process. Case.checkImgExist.confDlg.doesntExist.title=Missing Image Case.addImg.exception.msg=Error adding image to the case Case.updateCaseName.exception.msg=Error while trying to update the case name. @@ -269,12 +264,9 @@ Case.GetCaseTypeGivenPath.Failure=Unable to get case type Case.metaDataFileCorrupt.exception.msg=The case metadata file (.aut) is corrupted. Case.deleteReports.deleteFromDiskException.log.msg=Unable to delete the report from the disk. Case.deleteReports.deleteFromDiskException.msg=Unable to delete the report {0} from the disk.\nYou may manually delete it from {1} -CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \n\ - Case Name: {0}\n\ - Case Directory: {1} +CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \nCase Name: {0}\nCase Directory: {1} CaseDeleteAction.closeConfMsg.title=Warning: Closing the Current Case -CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\n\ -Close the folder and file and try again or you can delete the case manually. +CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\nClose the folder and file and try again or you can delete the case manually. CaseDeleteAction.msgDlg.fileInUse.title=Error: Folder In Use CaseDeleteAction.msgDlg.caseDelete.msg=Case {0} has been deleted. CaseOpenAction.autFilter.title={0} Case File ( {1}) @@ -306,8 +298,7 @@ NewCaseWizardAction.databaseProblem1.text=Cannot open database. Cancelling case NewCaseWizardAction.databaseProblem2.text=Error NewCaseWizardPanel1.validate.errMsg.invalidSymbols=The Case Name cannot contain any of the following symbols: \\ / : * ? " < > | NewCaseWizardPanel1.validate.errMsg.dirExists=Case directory ''{0}'' already exists. -NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\n\ - Do you want to create that directory? +NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\nDo you want to create that directory? NewCaseWizardPanel1.validate.confMsg.createDir.title=Create directory NewCaseWizardPanel1.validate.errMsg.cantCreateParDir.msg=Error: Could not create case parent directory {0} NewCaseWizardPanel1.validate.errMsg.prevCreateBaseDir.msg=Prevented from creating base directory {0} @@ -357,15 +348,15 @@ UnpackageWorker.doInBackground.errorFinding7zip=Could not locate 7-Zip executabl UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases UpdateRecentCases.menuItem.empty=-Empty- AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel -NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive -NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on \"C:\" drive. Case folder is created on the target system +NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on "C:" drive +NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on "C:" drive. Case folder is created on the target system NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning: Path to case folder is on the target system. Create case folder in mounted drive. CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1} MissingImageDialog.lbWarning.text= MissingImageDialog.lbWarning.toolTipText= NewCaseVisualPanel1.caseParentDirWarningLabel.text= -NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user +NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user\t\t NewCaseVisualPanel1.singleUserCaseRadioButton.text=Single-user NewCaseVisualPanel1.caseTypeLabel.text=Case Type: SingleUserCaseConverter.BadDatabaseFileName=Database file does not exist! diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index bde9bb7354..6e50328998 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -138,8 +138,7 @@ import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException; */ public class Case { - private static final int SHARED_CASE_LOCK_TIMEOUT_SECONDS = 30; - private static final int EXCLUSIVE_CASE_LOCK_TIMEOUT_MINS = 1; + private static final int CASE_LOCK_TIMEOUT_MINS = 1; private static final int CASE_RESOURCES_LOCK_TIMEOUT_HOURS = 1; private static final String SINGLE_USER_CASE_DB_NAME = "autopsy.db"; private static final String EVENT_CHANNEL_NAME = "%s-Case-Events"; //NON-NLS @@ -1845,7 +1844,7 @@ public class Case { caseAction.execute(progressIndicator, additionalParams); } else { acquireCaseLock(caseLockType); - try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) { + try (CoordinationService.Lock resourcesLock = acquireCaseResourcesLock(metadata.getCaseDirectory())) { if (null == resourcesLock) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock()); } @@ -2512,7 +2511,7 @@ public class Case { * resources. */ progressIndicator.progress(Bundle.Case_progressMessage_preparing()); - try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) { + try (CoordinationService.Lock resourcesLock = acquireCaseResourcesLock(metadata.getCaseDirectory())) { if (null == resourcesLock) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock()); } @@ -2652,16 +2651,16 @@ public class Case { * @throws CaseActionException If the lock cannot be acquired. */ @Messages({ - "Case.lockingException.couldNotAcquireSharedLock=Failed to get an exclusive lock on the case", - "Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a shared lock on the case" + "Case.lockingException.couldNotAcquireSharedLock=Failed to get an shared lock on the case.", + "Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a exclusive lock on the case." }) private void acquireCaseLock(CaseLockType lockType) throws CaseActionException { String caseDir = metadata.getCaseDirectory(); try { CoordinationService coordinationService = CoordinationService.getInstance(); caseLock = lockType == CaseLockType.SHARED - ? coordinationService.tryGetSharedLock(CategoryNode.CASES, caseDir, SHARED_CASE_LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS) - : coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseDir, EXCLUSIVE_CASE_LOCK_TIMEOUT_MINS, TimeUnit.MINUTES); + ? coordinationService.tryGetSharedLock(CategoryNode.CASES, caseDir, CASE_LOCK_TIMEOUT_MINS, TimeUnit.MINUTES) + : coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseDir, CASE_LOCK_TIMEOUT_MINS, TimeUnit.MINUTES); if (caseLock == null) { if (lockType == CaseLockType.SHARED) { throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireSharedLock()); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties-MERGED index 007af703c5..75866a79d0 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties-MERGED @@ -5,10 +5,7 @@ CentralRepoCommentDialog.title.addEditCentralRepoComment=Add/Edit Central Reposi OpenIDE-Module-Name=Central Repository OpenIDE-Module-Display-Category=Ingest Module OpenIDE-Module-Short-Description=Correlation Engine Ingest Module -OpenIDE-Module-Long-Description=\ - Correlation Engine ingest module and central database. \n\n\ - The Correlation Engine ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\ - Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest. +OpenIDE-Module-Long-Description=Correlation Engine ingest module and central database. \n\nThe Correlation Engine ingest module stores attributes of artifacts matching selected correlation types into a central database.\nStored attributes are used in future cases to correlate and analyzes files and artifacts during ingest. CentralRepoCommentDialog.commentLabel.text=Comment: CentralRepoCommentDialog.okButton.text=&OK CentralRepoCommentDialog.cancelButton.text=C&ancel diff --git a/Core/src/org/sleuthkit/autopsy/core/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/core/Bundle.properties-MERGED index 0b16a9701f..c84f1f1b86 100755 --- a/Core/src/org/sleuthkit/autopsy/core/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/core/Bundle.properties-MERGED @@ -3,13 +3,7 @@ Installer.closing.confirmationDialog.title=Ingest is Running # {0} - exception message Installer.closing.messageBox.caseCloseExceptionMessage=Error closing case: {0} OpenIDE-Module-Display-Category=Infrastructure -OpenIDE-Module-Long-Description=\ - This is the core Autopsy module.\n\n\ - The module contains the core components needed for the bare application to run; the RCP platform, windowing GUI, sleuthkit bindings, datamodel / storage, explorer, result viewers, content viewers, ingest framework, reporting, and core tools, such as the file search.\n\n\ - The framework included in the module contains APIs for developing modules for ingest, viewers and reporting. \ - The modules can be deployed as Plugins using the Autopsy plugin installer.\n\ - This module should not be uninstalled - without it, Autopsy will not run.\n\n\ - For more information, see http://www.sleuthkit.org/autopsy/ +OpenIDE-Module-Long-Description=This is the core Autopsy module.\n\nThe module contains the core components needed for the bare application to run; the RCP platform, windowing GUI, sleuthkit bindings, datamodel / storage, explorer, result viewers, content viewers, ingest framework, reporting, and core tools, such as the file search.\n\nThe framework included in the module contains APIs for developing modules for ingest, viewers and reporting. The modules can be deployed as Plugins using the Autopsy plugin installer.\nThis module should not be uninstalled - without it, Autopsy will not run.\n\nFor more information, see http://www.sleuthkit.org/autopsy/ OpenIDE-Module-Name=Autopsy-Core OpenIDE-Module-Short-Description=Autopsy Core Module org_sleuthkit_autopsy_core_update_center=http://sleuthkit.org/autopsy/updates.xml diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED index 9f363b7723..f252420726 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED @@ -63,9 +63,9 @@ DataContentViewerHex.totalPageLabel.text_1=100 DataContentViewerHex.pageLabel2.text=Page # Product Information panel -LBL_Description=
\n Product Version: {0} ({9})
Sleuth Kit Version: {7}
Netbeans RCP Build: {8}
Java: {1}; {2}
System: {3}; {4}; {5}
Userdir: {6}
+LBL_Description=
\n Product Version: {0} ({9})
Sleuth Kit Version: {7}
Netbeans RCP Build: {8}
Java: {1}; {2}
System: {3}; {4}; {5}
Userdir: {6}
Format_OperatingSystem_Value={0} version {1} running on {2} -LBL_Copyright=
Autopsy™ is a digital forensics platform based on The Sleuth Kit™ and other tools.
Copyright © 2003-2018.
+LBL_Copyright=
Autopsy™ is a digital forensics platform based on The Sleuth Kit™ and other tools.
Copyright © 2003-2018.
SortChooser.dialogTitle=Choose Sort Criteria ThumbnailViewChildren.progress.cancelling=(Cancelling) # {0} - file name @@ -95,7 +95,7 @@ DataResultViewerThumbnail.pageNextButton.text= DataResultViewerThumbnail.imagesLabel.text=Images: DataResultViewerThumbnail.imagesRangeLabel.text=- DataResultViewerThumbnail.pageNumLabel.text=- -DataResultViewerThumbnail.filePathLabel.text=\ \ \ +DataResultViewerThumbnail.filePathLabel.text=\ DataResultViewerThumbnail.goToPageLabel.text=Go to Page: DataResultViewerThumbnail.goToPageField.text= AdvancedConfigurationDialog.cancelButton.text=Cancel diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/coreutils/Bundle.properties-MERGED index a0d535f8e6..18e279dd2c 100755 --- a/Core/src/org/sleuthkit/autopsy/coreutils/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/coreutils/Bundle.properties-MERGED @@ -30,9 +30,7 @@ PlatformUtil.getProcVmUsed.sigarNotInit.msg=Cannot get virt mem used, sigar not PlatformUtil.getProcVmUsed.gen.msg=Cannot get virt mem used, {0} PlatformUtil.getJvmMemInfo.usageText=JVM heap usage: {0}, JVM non-heap usage: {1} PlatformUtil.getPhysicalMemInfo.usageText=Physical memory usage (max, total, free): {0}, {1}, {2} -PlatformUtil.getAllMemUsageInfo.usageText={0}\n\ -{1}\n\ -Process Virtual Memory: {2} +PlatformUtil.getAllMemUsageInfo.usageText={0}\n{1}\nProcess Virtual Memory: {2} # {0} - file name ReadImageTask.mesageText=Reading image: {0} StringExtract.illegalStateException.cannotInit.msg=Unicode table not properly initialized, cannot instantiate StringExtract diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED index 7f0a4d2419..e41321f306 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED @@ -43,7 +43,6 @@ ArtifactStringContent.attrsTableHeader.type=Type ArtifactStringContent.attrsTableHeader.value=Value ArtifactStringContent.failedToGetAttributes.message=Failed to get some or all attributes from case database ArtifactStringContent.failedToGetSourcePath.message=Failed to get source file path from case database -# {0} - node name BaseChildFactory.NoSuchEventBusException.message=No event bus for node: {0} BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details BlackboardArtifactNode.createSheet.artifactDetails.name=Result Details @@ -264,10 +263,10 @@ ImageNode.getActions.viewInNewWin.text=View in New Window ImageNode.createSheet.name.name=Name ImageNode.createSheet.name.displayName=Name ImageNode.createSheet.name.desc=no description -Installer.exception.tskVerStringNull.msg=Sleuth Kit JNI test call returned without error, but version string was null\! -Installer.exception.taskVerStringBang.msg=Sleuth Kit JNI test call returned without error, but version string was ""\! -Installer.tskLibErr.msg=Problem with Sleuth Kit JNI. Test call failed\!\n\nDetails: {0} -Installer.tskLibErr.err=Fatal Error\! +Installer.exception.tskVerStringNull.msg=Sleuth Kit JNI test call returned without error, but version string was null! +Installer.exception.taskVerStringBang.msg=Sleuth Kit JNI test call returned without error, but version string was ""! +Installer.tskLibErr.msg=Problem with Sleuth Kit JNI. Test call failed!\n\nDetails: {0} +Installer.tskLibErr.err=Fatal Error! InterestingHits.interestingItems.text=INTERESTING ITEMS InterestingHits.displayName.text=Interesting Items InterestingHits.createSheet.name.name=Name diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties-MERGED index 08cc69c39c..7ab8ecbe04 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties-MERGED @@ -14,7 +14,7 @@ KnownStatusSearchPanel.knownCheckBox.text=Known Status: KnownStatusSearchPanel.knownBadOptionCheckBox.text=Notable KnownStatusSearchPanel.knownOptionCheckBox.text=Known (NSRL or other) KnownStatusSearchPanel.unknownOptionCheckBox.text=Unknown -DateSearchFilter.noneSelectedMsg.text=At least one date type must be selected\! +DateSearchFilter.noneSelectedMsg.text=At least one date type must be selected! DateSearchPanel.dateCheckBox.text=Date: DateSearchPanel.jLabel4.text=Timezone: DateSearchPanel.jLabel3.text=*The date format is mm/dd/yyyy @@ -56,7 +56,7 @@ FileSearchPanel.search.results.details=Large number of matches may impact perfor FileSearchPanel.search.exception.noFilterSelected.msg=At least one filter must be selected. FileSearchPanel.search.validationErr.msg=Validation Error: {0} FileSearchPanel.emptyWhereClause.text=Invalid options, nothing to show. -KnownStatusSearchFilter.noneSelectedMsg.text=At least one known status must be selected\! +KnownStatusSearchFilter.noneSelectedMsg.text=At least one known status must be selected! NameSearchFilter.emptyNameMsg.text=Must enter something for name search. SearchNode.getName.text=Search Result SizeSearchPanel.sizeCompareComboBox.equalTo=equal to diff --git a/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties-MERGED index 9e4f612b6b..6be3e48e71 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties-MERGED @@ -140,7 +140,7 @@ IngestJob.cancelReason.outOfDiskSpace.text=Out of disk space IngestJob.cancelReason.servicesDown.text=Services Down IngestJob.cancelReason.caseClosed.text=Case closed IngestJobSettingsPanel.globalSettingsButton.text=Global Settings -gest +gest= IngestJobSettingsPanel.globalSettingsButton.actionCommand=Advanced IngestJobSettingsPanel.globalSettingsButton.text=Global Settings IngestJobSettingsPanel.pastJobsButton.text=History diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties-MERGED index d73865ac3e..4729293fb9 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties-MERGED @@ -11,12 +11,7 @@ ExtractArchiveWithPasswordAction.progress.text=Unpacking contents of archive: {0 ExtractArchiveWithPasswordAction.prompt.text=Enter Password ExtractArchiveWithPasswordAction.prompt.title=Enter Password OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=\ - Embedded File Extraction Ingest Module\n\nThe Embedded File Extraction Ingest Module processes document files (such as doc, docx, ppt, pptx, xls, xlsx) and archive files (such as zip and others archive types supported by the 7zip extractor).\n\ - Contents of these files are extracted and the derived files are added back to the current ingest to be processed by the configured ingest modules.\n\ - If the derived file happens to be an archive file, it will be re-processed by the 7zip extractor - the extractor will process archive files N-levels deep.\n\n\ - The extracted files are navigable in the directory tree.\n\n\ - The module is supported on Windows, Linux and Mac operating systems. +OpenIDE-Module-Long-Description=Embedded File Extraction Ingest Module\n\nThe Embedded File Extraction Ingest Module processes document files (such as doc, docx, ppt, pptx, xls, xlsx) and archive files (such as zip and others archive types supported by the 7zip extractor).\nContents of these files are extracted and the derived files are added back to the current ingest to be processed by the configured ingest modules.\nIf the derived file happens to be an archive file, it will be re-processed by the 7zip extractor - the extractor will process archive files N-levels deep.\n\nThe extracted files are navigable in the directory tree.\n\nThe module is supported on Windows, Linux and Mac operating systems. OpenIDE-Module-Name=Embedded File Extraction OpenIDE-Module-Short-Description=Embedded File Extraction Ingest Module EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.seek.exception.invalidOrigin=Invalid seek origin: {0} diff --git a/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED index 5971ab0d82..2fdb54d654 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED @@ -1,9 +1,7 @@ CannotRunFileTypeDetection=Cannot run file type detection. ExifParserFileIngestModule.indexError.message=Failed to post EXIF Metadata artifact(s). OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=\ - Exif metadata ingest module. \n\n\ - The ingest module analyzes image files, extracts Exif information and posts the Exif data as results. +OpenIDE-Module-Long-Description=Exif metadata ingest module. \n\nThe ingest module analyzes image files, extracts Exif information and posts the Exif data as results. OpenIDE-Module-Name=ExifParser OpenIDE-Module-Short-Description=Exif metadata ingest module ExifParserFileIngestModule.moduleName.text=Exif Parser diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle.properties-MERGED index cfaadf1635..5063bd55fa 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle.properties-MERGED @@ -36,27 +36,27 @@ FileExtMismatchSettingsPanel.jLabel1.text=File Types: FileExtMismatchSettingsPanel.newExtButton.text=New Extension FileExtMismatchSettingsPanel.newMimePrompt.message=Add a new MIME file type: FileExtMismatchSettingsPanel.newMimePrompt.title=New MIME -FileExtMismatchSettingsPanel.newMimePrompt.emptyMime.message=MIME type text is empty\! +FileExtMismatchSettingsPanel.newMimePrompt.emptyMime.message=MIME type text is empty! FileExtMismatchSettingsPanel.newMimePrompt.emptyMime.title=Empty type -FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotSupported.message=MIME type not supported\! +FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotSupported.message=MIME type not supported! FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotSupported.title=Type not supported -FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeExists.message=MIME type already exists\! +FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeExists.message=MIME type already exists! FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeExists.title=Type already exists FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotDetectable.message=MIME type is not detectable by this module. FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotDetectable.title=Type not detectable -FileExtMismatchSettingsPanel.removeTypeButton.noneSelected.message=No MIME type selected\! +FileExtMismatchSettingsPanel.removeTypeButton.noneSelected.message=No MIME type selected! FileExtMismatchSettingsPanel.removeTypeButton.noneSelected.title=No type selected FileExtMismatchSettingsPanel.newExtPrompt.message=Add an allowed extension: FileExtMismatchSettingsPanel.newExtPrompt.title=New allowed extension -FileExtMismatchSettingsPanel.newExtPrompt.empty.message=Extension text is empty\! +FileExtMismatchSettingsPanel.newExtPrompt.empty.message=Extension text is empty! FileExtMismatchSettingsPanel.newExtPrompt.empty.title=Extension text empty -FileExtMismatchSettingsPanel.newExtPrompt.noMimeType.message=No MIME type selected\! +FileExtMismatchSettingsPanel.newExtPrompt.noMimeType.message=No MIME type selected! FileExtMismatchSettingsPanel.newExtPrompt.noMimeType.title=No MIME type selected -FileExtMismatchSettingsPanel.newExtPrompt.extExists.message=Extension already exists\! +FileExtMismatchSettingsPanel.newExtPrompt.extExists.message=Extension already exists! FileExtMismatchSettingsPanel.newExtPrompt.extExists.title=Extension already exists -FileExtMismatchSettingsPanel.removeExtButton.noneSelected.message=No extension selected\! +FileExtMismatchSettingsPanel.removeExtButton.noneSelected.message=No extension selected! FileExtMismatchSettingsPanel.removeExtButton.noneSelected.title=No extension selected -FileExtMismatchSettingsPanel.removeExtButton.noMimeTypeSelected.message=No MIME type selected\! +FileExtMismatchSettingsPanel.removeExtButton.noMimeTypeSelected.message=No MIME type selected! FileExtMismatchSettingsPanel.removeExtButton.noMimeTypeSelected.title=No MIME type selected FileExtMismatchSettingsPanel.removeTypeButton.toolTipText= FileExtMismatchModuleSettingsPanel.checkAllRadioButton.text=Check all file types diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties-MERGED index 0b470ce6b1..44057d0016 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties-MERGED @@ -49,10 +49,7 @@ ImportCentralRepoDbProgressDialog.errorParsingFile.message=Error parsing hash se ImportCentralRepoDbProgressDialog.linesProcessed.message=\ hashes processed ImportCentralRepoDbProgressDialog.title.text=Central Repository Import Progress OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=\ - Hash Set ingest module. \n\n\ - The ingest module analyzes files in the disk image and marks them as "known" (based on NSRL hashset lookup for "known" files) and "bad / interesting" (based on one or more hash sets supplied by the user).\n\n\ - The module also contains additional non-ingest tools that are integrated in the GUI, such as file lookup by hash and hash set configuration. +OpenIDE-Module-Long-Description=Hash Set ingest module. \n\nThe ingest module analyzes files in the disk image and marks them as "known" (based on NSRL hashset lookup for "known" files) and "bad / interesting" (based on one or more hash sets supplied by the user).\n\nThe module also contains additional non-ingest tools that are integrated in the GUI, such as file lookup by hash and hash set configuration. OpenIDE-Module-Name=HashDatabases OptionsCategory_Name_HashDatabase=Hash Sets OptionsCategory_Keywords_HashDatabase=Hash Sets @@ -181,10 +178,7 @@ HashDbSearchThread.name.searching=Searching HashDbSearchThread.noMoreFilesWithMD5Msg=No other files with the same MD5 hash were found. ModalNoButtons.indexingDbsTitle=Indexing hash sets ModalNoButtons.indexingDbTitle=Indexing hash set -ModalNoButtons.exitHashDbIndexingMsg=You are about to exit out of indexing your hash sets. \n\ -The generated index will be left unusable. If you choose to continue,\n\ - please delete the corresponding -md5.idx file in the hash folder.\n\ - Exit indexing? +ModalNoButtons.exitHashDbIndexingMsg=You are about to exit out of indexing your hash sets. \nThe generated index will be left unusable. If you choose to continue,\nplease delete the corresponding -md5.idx file in the hash folder.\nExit indexing? ModalNoButtons.dlgTitle.unfinishedIndexing=Unfinished Indexing ModalNoButtons.indexThis.currentlyIndexing1Db=Currently indexing 1 hash set ModalNoButtons.indexThese.currentlyIndexing1OfNDbs=Currently indexing 1 of {0} diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED index 1279d3642b..31a0690b82 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED @@ -83,8 +83,8 @@ FilesSetRulePanel.nameTextField.text= FilesSetRulePanel.ruleNameLabel.text=Rule Name (Optional): FilesSetRulePanel.messages.emptyNameCondition=You must specify a name pattern for this rule. FilesSetRulePanel.messages.invalidNameRegex=The name regular expression is not valid:\n\n{0} -FilesSetRulePanel.messages.invalidCharInName=The name cannot contain \\, /, :, *, ?, \", <, or > unless it is a regular expression. -FilesSetRulePanel.messages.invalidCharInPath=The path cannot contain \\, :, *, ?, \", <, or > unless it is a regular expression. +FilesSetRulePanel.messages.invalidCharInName=The name cannot contain \\, /, :, *, ?, ", <, or > unless it is a regular expression. +FilesSetRulePanel.messages.invalidCharInPath=The path cannot contain \\, :, *, ?, ", <, or > unless it is a regular expression. FilesSetRulePanel.messages.invalidPathRegex=The path regular expression is not valid:\n\n{0} FilesSetDefsPanel.doFileSetsDialog.duplicateRuleSet.text=Rule set with name {0} already exists. FilesSetRulePanel.pathSeparatorInfoLabel.text=Folder must be in parent path. Use '/' to give consecutive names diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED index 87dacfc16c..2dc971a40d 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED @@ -21,7 +21,7 @@ PhotoRecIngestModule.complete.totalParsetime=Total Parsing Time: PhotoRecIngestModule.complete.photoRecResults=PhotoRec Results PhotoRecIngestModule.NotEnoughDiskSpace.detail.msg=PhotoRec error processing {0} with {1} Not enough space on primary disk to save unallocated space. PhotoRecIngestModule.cancelledByUser=PhotoRec cancelled by user. -PhotoRecIngestModule.error.exitValue=PhotoRec carver returned error exit value \= {0} when scanning {1} +PhotoRecIngestModule.error.exitValue=PhotoRec carver returned error exit value = {0} when scanning {1} PhotoRecIngestModule.error.msg=Error processing {0} with PhotoRec carver. PhotoRecIngestModule.complete.numberOfErrors=Number of Errors while Carving: PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text=PhotoRec Settings diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/html/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/report/modules/html/Bundle.properties-MERGED index 32f6867f0c..0be7595111 100755 --- a/Core/src/org/sleuthkit/autopsy/report/modules/html/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/report/modules/html/Bundle.properties-MERGED @@ -5,8 +5,8 @@ ReportHTML.getName.text=HTML Report ReportHTML.getDesc.text=A report about results and tagged items in HTML format. ReportHTML.writeIndex.title=for case {0} ReportHTML.writeIndex.noFrames.msg=Your browser is not compatible with our frame setup. -ReportHTML.writeIndex.noFrames.seeNav=Please see the navigation page for artifact links, -ReportHTML.writeIndex.seeSum=and the summary page for a case summary. +ReportHTML.writeIndex.noFrames.seeNav=Please see the navigation page for artifact links, +ReportHTML.writeIndex.seeSum=and the summary page for a case summary. ReportHTML.writeNav.title=Report Navigation ReportHTML.writeNav.h1=Report Navigation ReportHTML.writeNav.summary=Case Summary @@ -16,7 +16,7 @@ ReportHTML.writeSum.caseNumber=Case Number: ReportHTML.writeSum.caseNumImages=Number of Images: ReportHTML.writeSum.examiner=Examiner: ReportHTML.writeSum.title=Case Summary -ReportHTML.writeSum.warningMsg=Warning, this report was run before ingest services completed\! +ReportHTML.writeSum.warningMsg=Warning, this report was run before ingest services completed! # # autopsy/test/scripts/regression.py._html_report_diff() uses reportGenOn.text, caseName, caseNum, # examiner as a regex signature to skip report.html and summary.html diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index cd55f492ea..b20ccf5912 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Tue, 12 Nov 2019 16:58:46 -0500 +#Tue, 12 Nov 2019 17:21:46 -0500 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 06565e185c..998d3f715c 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Tue, 12 Nov 2019 16:58:46 -0500 +#Tue, 12 Nov 2019 17:21:46 -0500 CTL_MainWindow_Title=Autopsy 4.13.0 CTL_MainWindow_Title_No_Project=Autopsy 4.13.0 From b433a4b5dff7bf9ae0fb3d89a1af3bc86ac9e53a Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 12 Nov 2019 17:33:25 -0500 Subject: [PATCH 37/37] Data source deletion support --- .../casemodule/Bundle.properties-MERGED | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 75145f8a17..afa4459a85 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -52,8 +52,8 @@ Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}. Case.exceptionMessage.metadataUpdateError=Failed to update case metadata # {0} - exception message Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}. -Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a shared lock on the case -Case.lockingException.couldNotAcquireSharedLock=Failed to get an exclusive lock on the case +Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a exclusive lock on the case. +Case.lockingException.couldNotAcquireSharedLock=Failed to get an shared lock on the case. 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.progressIndicatorTitle.closingCase=Closing Case @@ -242,10 +242,15 @@ AddImageWizardIngestConfigPanel.dsProcDone.errs.text=*Errors encountered in addi AddImageWizardIngestConfigVisual.getName.text=Configure Ingest Modules AddImageWizardIterator.stepXofN=Step {0} of {1} AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1} -Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open! +Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open\! Case.open.msgDlg.updated.msg=Updated case database schema.\nA backup copy of the database with the following path has been made:\n {0} Case.open.msgDlg.updated.title=Case Database Schema Update -Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \nthis case are missing. Would you like to search for them now?\nPreviously, the image was located at:\n{0}\nPlease note that you will still be able to browse directories and generate reports\nif you choose No, but you will not be able to view file content or run the ingest process. +Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \n\ +this case are missing. Would you like to search for them now?\n\ +Previously, the image was located at:\n\ +{0}\n\ +Please note that you will still be able to browse directories and generate reports\n\ +if you choose No, but you will not be able to view file content or run the ingest process. Case.checkImgExist.confDlg.doesntExist.title=Missing Image Case.addImg.exception.msg=Error adding image to the case Case.updateCaseName.exception.msg=Error while trying to update the case name. @@ -264,9 +269,12 @@ Case.GetCaseTypeGivenPath.Failure=Unable to get case type Case.metaDataFileCorrupt.exception.msg=The case metadata file (.aut) is corrupted. Case.deleteReports.deleteFromDiskException.log.msg=Unable to delete the report from the disk. Case.deleteReports.deleteFromDiskException.msg=Unable to delete the report {0} from the disk.\nYou may manually delete it from {1} -CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \nCase Name: {0}\nCase Directory: {1} +CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \n\ + Case Name: {0}\n\ + Case Directory: {1} CaseDeleteAction.closeConfMsg.title=Warning: Closing the Current Case -CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\nClose the folder and file and try again or you can delete the case manually. +CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\n\ +Close the folder and file and try again or you can delete the case manually. CaseDeleteAction.msgDlg.fileInUse.title=Error: Folder In Use CaseDeleteAction.msgDlg.caseDelete.msg=Case {0} has been deleted. CaseOpenAction.autFilter.title={0} Case File ( {1}) @@ -298,7 +306,8 @@ NewCaseWizardAction.databaseProblem1.text=Cannot open database. Cancelling case NewCaseWizardAction.databaseProblem2.text=Error NewCaseWizardPanel1.validate.errMsg.invalidSymbols=The Case Name cannot contain any of the following symbols: \\ / : * ? " < > | NewCaseWizardPanel1.validate.errMsg.dirExists=Case directory ''{0}'' already exists. -NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\nDo you want to create that directory? +NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\n\ + Do you want to create that directory? NewCaseWizardPanel1.validate.confMsg.createDir.title=Create directory NewCaseWizardPanel1.validate.errMsg.cantCreateParDir.msg=Error: Could not create case parent directory {0} NewCaseWizardPanel1.validate.errMsg.prevCreateBaseDir.msg=Prevented from creating base directory {0} @@ -348,15 +357,15 @@ UnpackageWorker.doInBackground.errorFinding7zip=Could not locate 7-Zip executabl UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases UpdateRecentCases.menuItem.empty=-Empty- AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel -NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on "C:" drive -NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on "C:" drive. Case folder is created on the target system +NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive +NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on \"C:\" drive. Case folder is created on the target system NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning: Path to case folder is on the target system. Create case folder in mounted drive. CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1} MissingImageDialog.lbWarning.text= MissingImageDialog.lbWarning.toolTipText= NewCaseVisualPanel1.caseParentDirWarningLabel.text= -NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user\t\t +NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user NewCaseVisualPanel1.singleUserCaseRadioButton.text=Single-user NewCaseVisualPanel1.caseTypeLabel.text=Case Type: SingleUserCaseConverter.BadDatabaseFileName=Database file does not exist!