From 762ca5896ae4943e3fdfa5f66c2a80e747ccaa0f Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 20 May 2019 20:12:42 -0400 Subject: [PATCH 01/81] Intial commit of case database src delete Intial commit of the database src delete code --- .../autopsy/actions/Bundle.properties-MERGED | 2 + .../actions/DeleteDataSourceAction.java | 60 +++++++++++++++++++ .../autopsy/datamodel/ImageNode.java | 25 +++++++- 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java diff --git a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED index 506786c42d..757d2db120 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED @@ -23,6 +23,7 @@ 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}. @@ -31,6 +32,7 @@ DeleteFileBlackboardArtifactTagAction.deleteTags.alert=Unable to untag artifact DeleteFileContentTagAction.deleteTag=Remove File Tag # {0} - fileID DeleteFileContentTagAction.deleteTag.alert=Unable to untag file {0}. +ErrorDeletingDataSource.name.text=Error Deleting Data Source ExitAction.confirmationDialog.message=Ingest is running, are you sure you want to exit? ExitAction.confirmationDialog.title=Ingest is Running # {0} - exception message diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java new file mode 100644 index 0000000000..ec3def640c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -0,0 +1,60 @@ +/* + * 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.text.MessageFormat; +import java.util.logging.Level; +import javax.swing.AbstractAction; +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.datamodel.TskCoreException; +import org.sleuthkit.datamodel.VersionNumber; + +/** + * + * @author markm + */ +public final class DeleteDataSourceAction extends AbstractAction { + private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName()); + private static Long selectDataSource; + + @NbBundle.Messages({"DeleteDataSourceAction.name.text=Delete Data Source"}) + public DeleteDataSourceAction(Long selectedDataSource) { + super(Bundle.DeleteDataSourceAction_name_text()); + selectDataSource = selectedDataSource; + + } + @NbBundle.Messages({"ErrorDeletingDataSource.name.text=Error Deleting Data Source"}) + @Override + public void actionPerformed(ActionEvent event) { + try { + //VersionNumber checkVersionNumber = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaVersion(); + Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectDataSource); + } catch (NoCurrentCaseException | TskCoreException e) { + String msg = MessageFormat.format(Bundle.ErrorDeletingDataSource_name_text(), selectDataSource); + logger.log(Level.WARNING, msg, e); + //throw new TskCoreException(msg, e); + } + } + + +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 06560e6b13..3f72a67a93 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -32,6 +32,7 @@ import org.openide.nodes.Children; 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.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction; @@ -99,7 +100,7 @@ public class ImageNode extends AbstractContentNode { */ @Override @Messages({"ImageNode.action.runIngestMods.text=Run Ingest Modules", - "ImageNode.getActions.openFileSearchByAttr.text=Open File Search by Attributes",}) + "ImageNode.getActions.openFileSearchByAttr.text=Open File Search by Attributes"}) public Action[] getActions(boolean context) { List actionsList = new ArrayList<>(); @@ -113,6 +114,9 @@ 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()) { + actionsList.add(new DeleteDataSourceAction(content.getId())); + } return actionsList.toArray(new Action[0]); } @@ -200,6 +204,25 @@ public class ImageNode extends AbstractContentNode { return getClass().getName(); } + private final Boolean checkSchemaVersion() { + String sqlStatement = "SELECT a.value creationMajorVersion, b.value creationMinorVersion FROM tsk_db_info_extended a, tsk_db_info_extended b " + + " WHERE a.name = 'CREATION_SCHEMA_MAJOR_VERSION' and b.name = 'CREATION_SCHEMA_MINOR_VERSION';"; + try (CaseDbQuery query = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(sqlStatement);) { + ResultSet schemaVersion = query.getResultSet(); + while (schemaVersion.next()) { + int creationMajorVersion = schemaVersion.getInt("creationMajorVersion"); + int creationMinorVersion = schemaVersion.getInt("creationMinorVersion"); + if ((creationMajorVersion == 8 && creationMinorVersion >= 3) || creationMajorVersion > 8) { + return true; + } + } + } catch (SQLException | TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Failed to get the Create Major and Minor Schema Versions", ex); + } + + return false; + } + /* * 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 6bc08edf8a3f76d30c1645520724aa89de2c6624 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 20 May 2019 20:42:52 -0400 Subject: [PATCH 02/81] Codacy Fixes Make codacy go to its happy place. --- .../org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java | 4 +--- Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index ec3def640c..c81e81e3fb 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -27,7 +27,6 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.VersionNumber; /** * @@ -35,7 +34,7 @@ import org.sleuthkit.datamodel.VersionNumber; */ public final class DeleteDataSourceAction extends AbstractAction { private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName()); - private static Long selectDataSource; + private final Long selectDataSource; @NbBundle.Messages({"DeleteDataSourceAction.name.text=Delete Data Source"}) public DeleteDataSourceAction(Long selectedDataSource) { @@ -56,5 +55,4 @@ public final class DeleteDataSourceAction extends AbstractAction { } } - } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 3f72a67a93..5a2a052687 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -204,7 +204,7 @@ public class ImageNode extends AbstractContentNode { return getClass().getName(); } - private final Boolean checkSchemaVersion() { + private Boolean checkSchemaVersion() { String sqlStatement = "SELECT a.value creationMajorVersion, b.value creationMinorVersion FROM tsk_db_info_extended a, tsk_db_info_extended b " + " WHERE a.name = 'CREATION_SCHEMA_MAJOR_VERSION' and b.name = 'CREATION_SCHEMA_MINOR_VERSION';"; try (CaseDbQuery query = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(sqlStatement);) { @@ -216,6 +216,7 @@ public class ImageNode extends AbstractContentNode { return true; } } + schemaVersion.close(); } catch (SQLException | TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Failed to get the Create Major and Minor Schema Versions", ex); } From 9e905898eb60903f66271e5a64523e5b197900c5 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 20 May 2019 20:55:53 -0400 Subject: [PATCH 03/81] Update ImageNode.java --- Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 5a2a052687..d5d5d1bd04 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -213,6 +213,7 @@ public class ImageNode extends AbstractContentNode { int creationMajorVersion = schemaVersion.getInt("creationMajorVersion"); int creationMinorVersion = schemaVersion.getInt("creationMinorVersion"); if ((creationMajorVersion == 8 && creationMinorVersion >= 3) || creationMajorVersion > 8) { + schemaVersion.close(); return true; } } From 257cb46125c833389c91deb169287ff22409af7b Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 21 May 2019 16:08:02 -0400 Subject: [PATCH 04/81] Initial code for deleting a data source from SOLR This is the initial code for deleting a data source id from SOLR. --- .../actions/DeleteDataSourceAction.java | 18 +++++++++-- .../KeywordSearchService.java | 9 ++++++ .../keywordsearch/Bundle.properties-MERGED | 1 + .../autopsy/keywordsearch/Server.java | 32 +++++++++++++++++++ .../keywordsearch/SolrSearchService.java | 22 +++++++++++++ 5 files changed, 80 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index c81e81e3fb..4557cddaef 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -22,10 +22,15 @@ import java.awt.event.ActionEvent; import java.text.MessageFormat; 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.CaseMetadata; 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.autopsy.progress.ProgressIndicator; import org.sleuthkit.datamodel.TskCoreException; /** @@ -48,11 +53,20 @@ public final class DeleteDataSourceAction extends AbstractAction { try { //VersionNumber checkVersionNumber = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaVersion(); Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectDataSource); - } catch (NoCurrentCaseException | TskCoreException e) { + 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); + kwsService.deleteDataSource(dataSourceId); + } catch (KeywordSearchServiceException e) { + logger.log(Level.WARNING, "KWS Error", e); + } + + } } diff --git a/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java b/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java index 1a9e947b76..6610db8178 100644 --- a/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java +++ b/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java @@ -71,4 +71,13 @@ public interface KeywordSearchService extends Closeable { */ public void deleteTextIndex(CaseMetadata metadata) throws KeywordSearchServiceException; + /** + * Deletes the keyword search text for a specific data source. + * + * @param dataSourceId The data source id to be deleted from Solr. + * + * @throws KeywordSearchServiceException if unable to delete. + */ + public void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException; + } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED index c3a2ee4faf..c02e0ddba1 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED @@ -347,6 +347,7 @@ SolrSearch.openCore.msg=Opening text index SolrSearch.openGiantCore.msg=Opening text index. Text index for this case is very large and may take long time to load. SolrSearch.openLargeCore.msg=Opening text index. This may take several minutes. SolrSearch.readingIndexes.msg=Reading text index metadata file +SolrSearchService.deleteDataSource.exceptionMessage.noCurrentSolrCore=DeleteDataSource did not contain a current Solr core so could not delete the Data Source # {0} - index folder path SolrSearchService.exceptionMessage.failedToDeleteIndexFiles=Failed to delete text index files at {0} SolrSearchService.exceptionMessage.noCurrentSolrCore=IndexMetadata did not contain a current Solr core so could not delete the case diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index 0234ac7f73..bcc975c3ad 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -54,6 +54,7 @@ import org.apache.solr.client.solrj.request.CoreAdminRequest; import org.apache.solr.client.solrj.response.CoreAdminResponse; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.response.TermsResponse; +import org.apache.solr.client.solrj.response.UpdateResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrException; @@ -1215,6 +1216,25 @@ public class Server { } } + /** + * Delete a data source fo SOLR. + * + * @param dataSourceId to delete + * + * @throws NoOpenCoreException + */ + public void deleteDataSource(Long dataSourceId) throws NoOpenCoreException { + currentCoreLock.writeLock().lock(); + try { + if (null == currentCore) { + throw new NoOpenCoreException(); + } + currentCore.deleteDataSource(dataSourceId); + } finally { + currentCoreLock.writeLock().unlock(); + } + } + /** * Get the text contents of the given file as stored in SOLR. * @@ -1456,6 +1476,18 @@ public class Server { } } + private void deleteDataSource(Long dsObjId) { + String dataSourceId = Long.toString(dsObjId); + String deleteQuery = "image_id:" + dataSourceId; + try { + // Get the first result. + UpdateResponse updateResponse = solrCore.deleteByQuery(deleteQuery); + int x = 0; + } catch (SolrServerException | IOException ex) { + logger.log(Level.SEVERE, "Error deleting content from Solr. Solr image id " + dataSourceId, ex); //NON-NLS + } + } + void addDocument(SolrInputDocument doc) throws KeywordSearchModuleException { try { solrCore.add(doc); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java index be47a307fc..04a3ee18ba 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java @@ -191,6 +191,28 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService { } } + /** + * Deletes a data source from Solr for a case. + * + * @param dataSourceId the id of the data source to delete. + * + * @throws org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException + */ + @NbBundle.Messages({ + "SolrSearchService.deleteDataSource.exceptionMessage.noCurrentSolrCore=DeleteDataSource did not contain a current Solr core so could not delete the Data Source", + }) + @Override + public void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException { + try { + KeywordSearch.getServer().deleteDataSource(dataSourceId); + } catch (NoOpenCoreException ex) { + logger.log(Level.WARNING, NbBundle.getMessage(SolrSearchService.class, + "SolrSearchService.deleteDataSource.exceptionMessage.noCurrentSolrCore")); + throw new KeywordSearchServiceException(NbBundle.getMessage(SolrSearchService.class, + "SolrSearchService.deleteDataSource.exceptionMessage.noCurrentSolrCore")); + } + } + /** * Deletes Solr core for a case. * From a53ae96598851ae8e0ac51149a0498ff478d9e65 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Wed, 22 May 2019 13:45:27 -0400 Subject: [PATCH 05/81] Fix possible NPE Fix possible NPE if content was null. Skip statement and continue. --- .../src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java index 66d0803812..3d847c6521 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java @@ -220,7 +220,7 @@ class QueryResults { logger.log(Level.SEVERE, "Failed to get text source object for ", tskCoreException); //NON-NLS } - if (saveResults) { + if ((saveResults) && (content != null)) { /* * Post an artifact for the hit to the blackboard. */ From e7e82db60a903f0bb09b8dbd456d26716892ec53 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Wed, 22 May 2019 13:46:18 -0400 Subject: [PATCH 06/81] Add commit to solr deletion Add commit to solr image id deletion --- .../src/org/sleuthkit/autopsy/keywordsearch/Server.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index bcc975c3ad..74a7c294df 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -1230,6 +1230,9 @@ public class Server { throw new NoOpenCoreException(); } currentCore.deleteDataSource(dataSourceId); + currentCore.commit(); + } catch (SolrServerException ex) { + logger.log(Level.SEVERE, "Solr delete data dource failed for data source: " + Long.toString(dataSourceId), ex); //NON-NLS } finally { currentCoreLock.writeLock().unlock(); } @@ -1482,7 +1485,6 @@ public class Server { try { // Get the first result. UpdateResponse updateResponse = solrCore.deleteByQuery(deleteQuery); - int x = 0; } catch (SolrServerException | IOException ex) { logger.log(Level.SEVERE, "Error deleting content from Solr. Solr image id " + dataSourceId, ex); //NON-NLS } From 6b0891be6b389f31041e33e052851ee699cb8df5 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Wed, 22 May 2019 15:53:22 -0400 Subject: [PATCH 07/81] create and publish event when a data source is deleted Code to create and publish an event when a data source is deleted. --- .../actions/DeleteDataSourceAction.java | 1 + .../sleuthkit/autopsy/casemodule/Case.java | 14 ++++++ .../events/DataSourceDeletedEvent.java | 44 +++++++++++++++++++ 3 files changed, 59 insertions(+) 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 4557cddaef..4170329e2b 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -54,6 +54,7 @@ public final class DeleteDataSourceAction extends AbstractAction { //VersionNumber checkVersionNumber = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaVersion(); Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectDataSource); deleteDataSource(selectDataSource); + Case.getCurrentCaseThrows().notifyDataSourceDeleted(); } catch (NoCurrentCaseException | TskCoreException | KeywordSearchServiceException e) { String msg = MessageFormat.format(Bundle.ErrorDeletingDataSource_name_text(), selectDataSource); logger.log(Level.WARNING, msg, e); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index c9640f1a13..b3cbe76595 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; @@ -1446,6 +1447,19 @@ public class Case { eventPublisher.publish(new DataSourceNameChangedEvent(dataSource, newName)); } + /** + * Notifies case event subscribers that a data source has been delete from + * the case database. + * + * This should not be called from the event dispatch thread (EDT) + * + * @param dataSource The data source. + * @param newName The new name for the data source + */ + public void notifyDataSourceDeleted() { + eventPublisher.publish(new DataSourceDeletedEvent()); + } + /** * Notifies case event subscribers that a content tag has been added. * 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..2822b82a5a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java @@ -0,0 +1,44 @@ +/* + * 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 java.util.logging.Level; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.events.AutopsyEvent; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +public class DataSourceDeletedEvent extends AutopsyEvent implements Serializable { + + /** + * Constructs an event published when a data source is added to a case. + * + * @param dataSource The data source that was added. + * @param newName The new name of the data source + */ + public DataSourceDeletedEvent() { + + super(Case.Events.DATA_SOURCE_DELETED.toString(), null, null); + + } +} From 97ec082f8cd67f958526bb6964886a34bd02917b Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Thu, 23 May 2019 08:43:30 -0400 Subject: [PATCH 08/81] Update DataSourceDeletedEvent.java Added Data Source id --- .../events/DataSourceDeletedEvent.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java index 2822b82a5a..03a5ce7438 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java @@ -20,25 +20,32 @@ package org.sleuthkit.autopsy.casemodule.events; import java.io.Serializable; -import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.events.AutopsyEvent; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; 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 dataSource The data source that was added. - * @param newName The new name of the data source + * @param dataSourceId The data source that was deleted. */ - public DataSourceDeletedEvent() { + public DataSourceDeletedEvent(Long dataSourceId) { - super(Case.Events.DATA_SOURCE_DELETED.toString(), null, null); - + 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 getDataSourced() { + return dataSourceId; + } + } From ac6429c11a2f0ac601b66d58443f996da43b19d1 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Thu, 23 May 2019 08:45:35 -0400 Subject: [PATCH 09/81] Update Case.java Added data source Id --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index b3cbe76595..8fe2b22187 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -1453,11 +1453,10 @@ public class Case { * * This should not be called from the event dispatch thread (EDT) * - * @param dataSource The data source. - * @param newName The new name for the data source + * @param dataSourceId The data source that was deleted. */ - public void notifyDataSourceDeleted() { - eventPublisher.publish(new DataSourceDeletedEvent()); + public void notifyDataSourceDeleted(Long dataSourceId) { + eventPublisher.publish(new DataSourceDeletedEvent(dataSourceId)); } /** From b0ef89b2c9f0e6809105dcec3a973fe352ea61a8 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Thu, 23 May 2019 08:50:30 -0400 Subject: [PATCH 10/81] Update DataSourceDeletedEvent.java Fix typo --- .../autopsy/casemodule/events/DataSourceDeletedEvent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java index 03a5ce7438..5317a951d0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java @@ -44,7 +44,7 @@ public class DataSourceDeletedEvent extends AutopsyEvent implements Serializable * * @return The data source id. */ - public long getDataSourced() { + public long getDataSourceId() { return dataSourceId; } From 3887c9b1108a4f4239ddaf8782a614091394f669 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Sat, 25 May 2019 22:18:52 -0400 Subject: [PATCH 11/81] Update DeleteDataSourceAction.java Remove comment. Add data source id to event. --- .../autopsy/actions/DeleteDataSourceAction.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index 4170329e2b..0b345eda5f 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -33,18 +33,14 @@ 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; + private final Long selectedDataSource; @NbBundle.Messages({"DeleteDataSourceAction.name.text=Delete Data Source"}) public DeleteDataSourceAction(Long selectedDataSource) { super(Bundle.DeleteDataSourceAction_name_text()); - selectDataSource = selectedDataSource; + this.selectedDataSource = selectedDataSource; } @NbBundle.Messages({"ErrorDeletingDataSource.name.text=Error Deleting Data Source"}) @@ -52,11 +48,11 @@ public final class DeleteDataSourceAction extends AbstractAction { public void actionPerformed(ActionEvent event) { try { //VersionNumber checkVersionNumber = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaVersion(); - Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectDataSource); - deleteDataSource(selectDataSource); - Case.getCurrentCaseThrows().notifyDataSourceDeleted(); + Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectedDataSource); + deleteDataSource(selectedDataSource); + Case.getCurrentCaseThrows().notifyDataSourceDeleted(selectedDataSource); } catch (NoCurrentCaseException | TskCoreException | KeywordSearchServiceException e) { - String msg = MessageFormat.format(Bundle.ErrorDeletingDataSource_name_text(), selectDataSource); + String msg = MessageFormat.format(Bundle.ErrorDeletingDataSource_name_text(), selectedDataSource); logger.log(Level.WARNING, msg, e); //throw new TskCoreException(msg, e); } From 732ac99284062cc22403574632cbf5274fada852 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Sat, 25 May 2019 22:20:34 -0400 Subject: [PATCH 12/81] initial code changes Add foreign Key constraints. Add event code to trigger Delete of data in drawable database. Changed schema version --- .../imagegallery/ImageGalleryModule.java | 9 ++++- .../imagegallery/datamodel/DrawableDB.java | 39 +++++++++++++++---- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java index 1c68d6c49d..d76b3f7486 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java @@ -37,6 +37,7 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; +import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.events.AutopsyEvent; @@ -70,7 +71,8 @@ public class ImageGalleryModule { Case.Events.CURRENT_CASE, Case.Events.DATA_SOURCE_ADDED, Case.Events.CONTENT_TAG_ADDED, - Case.Events.CONTENT_TAG_DELETED + Case.Events.CONTENT_TAG_DELETED, + Case.Events.DATA_SOURCE_DELETED ); private static final Object controllerLock = new Object(); @GuardedBy("controllerLock") @@ -317,6 +319,11 @@ public class ImageGalleryModule { currentController.getTagsManager().fireTagDeletedEvent(tagDeletedEvent); } // RJCTODO: Why not remove the tag from the cache? break; + case DATA_SOURCE_DELETED: + final DataSourceDeletedEvent dataSourceDeletedEvent = (DataSourceDeletedEvent) event; + long dataSoureObjId = dataSourceDeletedEvent.getDataSourceId(); + drawableDB = currentController.getDatabase(); + drawableDB.deleteDataSource(dataSoureObjId); default: logger.log(Level.SEVERE, String.format("Received %s event with no subscription", event.getPropertyName())); //NON-NLS break; diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java index 1881281475..037a2004a8 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java @@ -106,8 +106,8 @@ public final class DrawableDB { private static final String IG_CREATION_SCHEMA_MAJOR_VERSION_KEY = "IG_CREATION_SCHEMA_MAJOR_VERSION"; private static final String IG_CREATION_SCHEMA_MINOR_VERSION_KEY = "IG_CREATION_SCHEMA_MINOR_VERSION"; - private static final VersionNumber IG_STARTING_SCHEMA_VERSION = new VersionNumber(1, 0, 0); // IG Schema Starting version - private static final VersionNumber IG_SCHEMA_VERSION = new VersionNumber(1, 1, 0); // IG Schema Current version + private static final VersionNumber IG_STARTING_SCHEMA_VERSION = new VersionNumber(1, 2, 0); // IG Schema Starting version + private static final VersionNumber IG_SCHEMA_VERSION = new VersionNumber(1, 2, 0); // IG Schema Current version private PreparedStatement insertHashSetStmt; @@ -145,6 +145,8 @@ public final class DrawableDB { private PreparedStatement hashSetGroupStmt; private PreparedStatement pathGroupFilterByDataSrcStmt; + + private PreparedStatement deleteDataSourceStmt; /** * map from {@link DrawableAttribute} to the {@link PreparedStatement} that @@ -263,6 +265,7 @@ public final class DrawableDB { selectHashSetStmt = prepareStatement("SELECT hash_set_id FROM hash_sets WHERE hash_set_name = ?"); //NON-NLS insertHashHitStmt = prepareStatement("INSERT OR IGNORE INTO hash_set_hits (hash_set_id, obj_id) VALUES (?,?)"); //NON-NLS removeHashHitStmt = prepareStatement("DELETE FROM hash_set_hits WHERE obj_id = ?"); //NON-NLS + deleteDataSourceStmt = prepareStatement("DELETE FROM datasources where ds_obj_id = ?"); //NON-NLS return true; } catch (TskCoreException | SQLException ex) { logger.log(Level.SEVERE, "Failed to prepare all statements", ex); //NON-NLS @@ -475,7 +478,7 @@ public final class DrawableDB { //allow to query while in transaction - no need read locks statement.execute("PRAGMA read_uncommitted = True;"); //NON-NLS - //TODO: do we need this? + //used for data source deletion statement.execute("PRAGMA foreign_keys = ON"); //NON-NLS //TODO: test this @@ -578,7 +581,8 @@ public final class DrawableDB { + " modified_time integer, " //NON-NLS + " make VARCHAR(255) DEFAULT NULL, " //NON-NLS + " model VARCHAR(255) DEFAULT NULL, " //NON-NLS - + " analyzed integer DEFAULT 0)"; //NON-NLS + + " analyzed integer DEFAULT 0, " //NON-NLS + + " FOREIGN KEY (data_source_obj_id) REFERENCES datasources(ds_obj_id) ON DELETE CASCADE)"; //NON-NLS stmt.execute(sql); } catch (SQLException ex) { logger.log(Level.SEVERE, "Failed to create drawable_files table", ex); //NON-NLS @@ -598,8 +602,9 @@ public final class DrawableDB { try { String sql = "CREATE TABLE if not exists hash_set_hits " //NON-NLS + "(hash_set_id INTEGER REFERENCES hash_sets(hash_set_id) not null, " //NON-NLS - + " obj_id BIGINT REFERENCES drawable_files(obj_id) not null, " //NON-NLS - + " PRIMARY KEY (hash_set_id, obj_id))"; //NON-NLS + + " obj_id BIGINT NOT NULL, " //NON-NLS + + " PRIMARY KEY (hash_set_id, obj_id), " //NON-NLS + + " FOREIGN KEY (obj_id) REFERENCES drawable_files(obj_id) ON DELETE CASCADE)"; //NON-NLS stmt.execute(sql); } catch (SQLException ex) { logger.log(Level.SEVERE, "Failed to create hash_set_hits table", ex); //NON-NLS @@ -710,7 +715,7 @@ public final class DrawableDB { + " examiner_id integer not null, " //NON-NLS + " seen integer DEFAULT 0, " //NON-NLS + " UNIQUE(group_id, examiner_id)," - + " FOREIGN KEY(group_id) REFERENCES " + GROUPS_TABLENAME + "(group_id)," + + " FOREIGN KEY(group_id) REFERENCES " + GROUPS_TABLENAME + "(group_id) ON DELETE CASCADE," + " FOREIGN KEY(examiner_id) REFERENCES tsk_examiners(examiner_id)" + " )"; //NON-NLS @@ -2027,6 +2032,26 @@ public final class DrawableDB { } } + /** + * delete the datasource from the database with cascade. + * + * @param id the obj_id of the row to be deleted + */ + public void deleteDataSource(long dataSourceId) { + dbWriteLock(); + DrawableTransaction trans = null; + try { + trans = beginTransaction(); + deleteDataSourceStmt.setLong(1, dataSourceId); + deleteDataSourceStmt.executeUpdate(); + commitTransaction(trans, true); + } catch (SQLException | TskCoreException ex) { + logger.log(Level.WARNING, "failed to deletesource for obj_id = " + dataSourceId, ex); //NON-NLS + } finally { + dbWriteUnlock(); + } + } + public class MultipleTransactionException extends IllegalStateException { public MultipleTransactionException() { From 67d39899d05aeec863d813f5a02c66901deef886 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Sun, 26 May 2019 15:54:02 -0400 Subject: [PATCH 13/81] Update DeleteDataSourceAction.java Codacy Fix --- .../org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index 4557cddaef..d7afe92c3d 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -25,12 +25,10 @@ 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.CaseMetadata; 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.autopsy.progress.ProgressIndicator; import org.sleuthkit.datamodel.TskCoreException; /** From 249ed2445697d053a1ff2efbb21f79c1f0836ab0 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Sun, 26 May 2019 16:00:48 -0400 Subject: [PATCH 14/81] Update DeleteDataSourceAction.java fix codacy --- .../org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index d7afe92c3d..60b9063bf9 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -31,10 +31,6 @@ import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException; 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; From 7d6fb729ef6d831f0a359d579cde0bba7d088219 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Wed, 29 May 2019 11:26:02 -0400 Subject: [PATCH 15/81] 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 16/81] 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 17/81] 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 18/81] 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 19/81] 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 20/81] 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 21/81] 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 d7800c6cc3e33ff8457a0b63a0d37006e76af5ea Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 15 Jul 2019 14:09:21 -0400 Subject: [PATCH 22/81] Update DeleteDataSourceAction.java Address comments --- .../actions/DeleteDataSourceAction.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index c81e81e3fb..0cde0e152e 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -28,30 +28,22 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; 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; + private final Long selectedDataSource; @NbBundle.Messages({"DeleteDataSourceAction.name.text=Delete Data Source"}) public DeleteDataSourceAction(Long selectedDataSource) { super(Bundle.DeleteDataSourceAction_name_text()); - selectDataSource = selectedDataSource; - + this.selectedDataSource = selectedDataSource; } - @NbBundle.Messages({"ErrorDeletingDataSource.name.text=Error Deleting Data Source"}) + @Override public void actionPerformed(ActionEvent event) { try { - //VersionNumber checkVersionNumber = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaVersion(); - Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectDataSource); + Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectedDataSource); } catch (NoCurrentCaseException | TskCoreException e) { - String msg = MessageFormat.format(Bundle.ErrorDeletingDataSource_name_text(), selectDataSource); - logger.log(Level.WARNING, msg, e); - //throw new TskCoreException(msg, e); + logger.log(Level.WARNING, "Error Deleting Data source " + selectedDataSource, e); } } From 710b59de7f5a9bd09f14381bee49c5a8b124d246 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 15 Jul 2019 14:13:21 -0400 Subject: [PATCH 23/81] Update DeleteDataSourceAction.java Added comment --- .../org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index 0cde0e152e..f0e3ae3c74 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.actions; import java.awt.event.ActionEvent; -import java.text.MessageFormat; import java.util.logging.Level; import javax.swing.AbstractAction; import org.openide.util.NbBundle; @@ -28,6 +27,9 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.TskCoreException; +/** + * Instances of this Action allow users to delete the specified data source. + */ public final class DeleteDataSourceAction extends AbstractAction { private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName()); private final Long selectedDataSource; From 798f345ff942b6e7b177876e4dfdd234cd1c2dc7 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 15 Jul 2019 17:23:05 -0400 Subject: [PATCH 24/81] Update ImageNode.java address comments --- Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index d5d5d1bd04..dbfa44e207 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -205,7 +205,7 @@ public class ImageNode extends AbstractContentNode { } private Boolean checkSchemaVersion() { - String sqlStatement = "SELECT a.value creationMajorVersion, b.value creationMinorVersion FROM tsk_db_info_extended a, tsk_db_info_extended b " + + String sqlStatement = "SELECT a.value AS creationMajorVersion, b.value AS creationMinorVersion FROM tsk_db_info_extended a, tsk_db_info_extended b " + " WHERE a.name = 'CREATION_SCHEMA_MAJOR_VERSION' and b.name = 'CREATION_SCHEMA_MINOR_VERSION';"; try (CaseDbQuery query = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(sqlStatement);) { ResultSet schemaVersion = query.getResultSet(); @@ -213,11 +213,9 @@ public class ImageNode extends AbstractContentNode { int creationMajorVersion = schemaVersion.getInt("creationMajorVersion"); int creationMinorVersion = schemaVersion.getInt("creationMinorVersion"); if ((creationMajorVersion == 8 && creationMinorVersion >= 3) || creationMajorVersion > 8) { - schemaVersion.close(); return true; } } - schemaVersion.close(); } catch (SQLException | TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Failed to get the Create Major and Minor Schema Versions", ex); } From 96ed64161d95245e20feac180bb2df314a8bdd21 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 15 Jul 2019 20:18:06 -0400 Subject: [PATCH 25/81] Update ImageNode.java Update comments from 2409 that were carried over. --- Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index d5d5d1bd04..dbfa44e207 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -205,7 +205,7 @@ public class ImageNode extends AbstractContentNode { } private Boolean checkSchemaVersion() { - String sqlStatement = "SELECT a.value creationMajorVersion, b.value creationMinorVersion FROM tsk_db_info_extended a, tsk_db_info_extended b " + + String sqlStatement = "SELECT a.value AS creationMajorVersion, b.value AS creationMinorVersion FROM tsk_db_info_extended a, tsk_db_info_extended b " + " WHERE a.name = 'CREATION_SCHEMA_MAJOR_VERSION' and b.name = 'CREATION_SCHEMA_MINOR_VERSION';"; try (CaseDbQuery query = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(sqlStatement);) { ResultSet schemaVersion = query.getResultSet(); @@ -213,11 +213,9 @@ public class ImageNode extends AbstractContentNode { int creationMajorVersion = schemaVersion.getInt("creationMajorVersion"); int creationMinorVersion = schemaVersion.getInt("creationMinorVersion"); if ((creationMajorVersion == 8 && creationMinorVersion >= 3) || creationMajorVersion > 8) { - schemaVersion.close(); return true; } } - schemaVersion.close(); } catch (SQLException | TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Failed to get the Create Major and Minor Schema Versions", ex); } From 8ddc174f2ff43a8d7f89671c4d8a066ff24cd2b6 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 15 Jul 2019 20:24:51 -0400 Subject: [PATCH 26/81] Update DeleteDataSourceAction.java adjust for comments in 2409 --- .../autopsy/actions/DeleteDataSourceAction.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index 60b9063bf9..abed76e98a 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.actions; import java.awt.event.ActionEvent; -import java.text.MessageFormat; import java.util.logging.Level; import javax.swing.AbstractAction; import org.openide.util.Lookup; @@ -33,25 +32,21 @@ import org.sleuthkit.datamodel.TskCoreException; public final class DeleteDataSourceAction extends AbstractAction { private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName()); - private final Long selectDataSource; + private final Long selectedDataSource; @NbBundle.Messages({"DeleteDataSourceAction.name.text=Delete Data Source"}) public DeleteDataSourceAction(Long selectedDataSource) { super(Bundle.DeleteDataSourceAction_name_text()); - selectDataSource = selectedDataSource; + this.selectedDataSource = selectedDataSource; } - @NbBundle.Messages({"ErrorDeletingDataSource.name.text=Error Deleting Data Source"}) @Override public void actionPerformed(ActionEvent event) { try { - //VersionNumber checkVersionNumber = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaVersion(); - Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectDataSource); - deleteDataSource(selectDataSource); + Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectedDataSource); + deleteDataSource(selectedDataSource); } 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); + logger.log(Level.WARNING, "Error Deleting Data source " + selectedDataSource, e); } } private static void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException { From 83991049637e89341d4df8159c8e3e9776e0bf2b Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 15 Jul 2019 20:34:23 -0400 Subject: [PATCH 27/81] Update Server.java Fix spelling error in comment and remove long conversion in msg. --- .../src/org/sleuthkit/autopsy/keywordsearch/Server.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index 74a7c294df..000c11a282 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -1217,7 +1217,7 @@ public class Server { } /** - * Delete a data source fo SOLR. + * Delete a data source from SOLR. * * @param dataSourceId to delete * @@ -1232,7 +1232,7 @@ public class Server { currentCore.deleteDataSource(dataSourceId); currentCore.commit(); } catch (SolrServerException ex) { - logger.log(Level.SEVERE, "Solr delete data dource failed for data source: " + Long.toString(dataSourceId), ex); //NON-NLS + logger.log(Level.SEVERE, "Solr delete data dource failed for data source: " + dataSourceId, ex); //NON-NLS } finally { currentCoreLock.writeLock().unlock(); } From ab8aa33b040bb19b3961958ddd6fdb27a3ce242a Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Thu, 18 Jul 2019 13:47:43 -0400 Subject: [PATCH 28/81] Update ImageNode.java removed sql per comment --- .../autopsy/datamodel/ImageNode.java | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index dbfa44e207..01e2d84458 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -205,23 +205,20 @@ public class ImageNode extends AbstractContentNode { } private Boolean checkSchemaVersion() { - String sqlStatement = "SELECT a.value AS creationMajorVersion, b.value AS creationMinorVersion FROM tsk_db_info_extended a, tsk_db_info_extended b " + - " WHERE a.name = 'CREATION_SCHEMA_MAJOR_VERSION' and b.name = 'CREATION_SCHEMA_MINOR_VERSION';"; - try (CaseDbQuery query = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(sqlStatement);) { - ResultSet schemaVersion = query.getResultSet(); - while (schemaVersion.next()) { - int creationMajorVersion = schemaVersion.getInt("creationMajorVersion"); - int creationMinorVersion = schemaVersion.getInt("creationMinorVersion"); - if ((creationMajorVersion == 8 && creationMinorVersion >= 3) || creationMajorVersion > 8) { - return true; - } + try { + int creationMajorVersion = Case.getCurrentCaseThrows().getSleuthkitCase().getDBCreationSchemaVersion().getMajor(); + int creationMinorVersion = Case.getCurrentCaseThrows().getSleuthkitCase().getDBCreationSchemaVersion().getMinor(); + + if ((creationMajorVersion == 8 && creationMinorVersion >= 3) || creationMajorVersion > 8) { + return true; } - } catch (SQLException | TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Failed to get the Create Major and Minor Schema Versions", ex); - } + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Failed to get creation schema version: ", ex); + } return false; - } + + } /* * This property change listener refreshes the tree when a new file is From 4dab9753f1a7cfde7c5a4d9d6e6c03210f58fcee Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 12 Aug 2019 19:52:39 -0400 Subject: [PATCH 29/81] Update ImageNode.java Change call to getDBSchemaCrationVersion so it is correct. --- Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 67a240433a..46100c9b04 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -210,8 +210,8 @@ public class ImageNode extends AbstractContentNode { private Boolean checkSchemaVersion() { try { - int creationMajorVersion = Case.getCurrentCaseThrows().getSleuthkitCase().getDBCreationSchemaVersion().getMajor(); - int creationMinorVersion = Case.getCurrentCaseThrows().getSleuthkitCase().getDBCreationSchemaVersion().getMinor(); + int creationMajorVersion = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaCreationVersion().getMajor(); + int creationMinorVersion = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaCreationVersion().getMinor(); if ((creationMajorVersion == 8 && creationMinorVersion >= 3) || creationMajorVersion > 8) { return true; From 2e6ce83634c06ad5a5d3425f56f562b893c19ded Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 12 Aug 2019 20:24:01 -0400 Subject: [PATCH 30/81] Update ImageNode.java Make one call to getDBCreationSchema instead of 2 to make it look tidier. --- Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 46100c9b04..18f5eef67b 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -51,6 +51,7 @@ 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; /** @@ -210,10 +211,9 @@ public class ImageNode extends AbstractContentNode { private Boolean checkSchemaVersion() { try { - int creationMajorVersion = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaCreationVersion().getMajor(); - int creationMinorVersion = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaCreationVersion().getMinor(); - - if ((creationMajorVersion == 8 && creationMinorVersion >= 3) || creationMajorVersion > 8) { + CaseDbSchemaVersionNumber creationVersion = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaCreationVersion(); + + if ((creationVersion.getMajor() == 8 && creationVersion.getMinor() >= 3) || creationVersion.getMajor() > 8) { return true; } } catch (NoCurrentCaseException ex) { From 1b16ffa54bb3c952abe9aac3496ca59358d2e472 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 13 Aug 2019 11:17:57 -0400 Subject: [PATCH 31/81] Update from merge of 2409 Update code from merge of 2409 --- .../actions/DeleteDataSourceAction.java | 126 +-- .../autopsy/datamodel/ImageNode.java | 696 ++++++++--------- .../autopsy/keywordsearch/QueryResults.java | 728 +++++++++--------- 3 files changed, 775 insertions(+), 775 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index f03cd446f1..697e37480b 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -1,63 +1,63 @@ -/* - * 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; - -/** - * Instances of this Action allow users to delete the specified data source. - */ -public final class DeleteDataSourceAction extends AbstractAction { - private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName()); - private final Long selectedDataSource; - - @NbBundle.Messages({"DeleteDataSourceAction.name.text=Delete Data Source"}) - public DeleteDataSourceAction(Long selectedDataSource) { - super(Bundle.DeleteDataSourceAction_name_text()); - this.selectedDataSource = selectedDataSource; - } - - @Override - public void actionPerformed(ActionEvent event) { - try { - Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectedDataSource); - } catch (NoCurrentCaseException | TskCoreException e) { - logger.log(Level.WARNING, "Error Deleting Data source " + selectedDataSource, e); - } - } - private static void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException { - try { - KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); - kwsService.deleteDataSource(dataSourceId); - } catch (KeywordSearchServiceException e) { - logger.log(Level.WARNING, "KWS Error", e); - } - - } -} +/* + * 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; + +/** + * Instances of this Action allow users to delete the specified data source. + */ +public final class DeleteDataSourceAction extends AbstractAction { + private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName()); + private final Long selectedDataSource; + + @NbBundle.Messages({"DeleteDataSourceAction.name.text=Delete Data Source"}) + public DeleteDataSourceAction(Long selectedDataSource) { + super(Bundle.DeleteDataSourceAction_name_text()); + this.selectedDataSource = selectedDataSource; + } + + @Override + public void actionPerformed(ActionEvent event) { + try { + Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectedDataSource); + } catch (NoCurrentCaseException | TskCoreException e) { + logger.log(Level.WARNING, "Error Deleting Data source " + selectedDataSource, e); + } + } + private static void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException { + try { + KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); + kwsService.deleteDataSource(dataSourceId); + } catch (KeywordSearchServiceException e) { + logger.log(Level.WARNING, "KWS Error", e); + } + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 3eefc3aeff..18f5eef67b 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -1,348 +1,348 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011-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.datamodel; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; -import java.util.logging.Level; -import javax.swing.Action; -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.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.directorytree.ExplorerNodeActionVisitor; -import org.sleuthkit.autopsy.directorytree.FileSearchAction; -import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; -import org.sleuthkit.autopsy.ingest.IngestManager; -import org.sleuthkit.autopsy.ingest.ModuleContentEvent; -import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.RunIngestModulesAction; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.Image; -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; - -/** - * This class is used to represent the "Node" for the image. The children of - * this node are volumes. - */ -public class ImageNode extends AbstractContentNode { - - private static final Logger logger = Logger.getLogger(ImageNode.class.getName()); - - /** - * Helper so that the display name and the name used in building the path - * are determined the same way. - * - * @param i Image to get the name of - * - * @return short name for the Image - */ - static String nameForImage(Image i) { - return i.getName(); - } - - /** - * @param img - */ - public ImageNode(Image img) { - super(img); - - // set name, display name, and icon - String imgName = nameForImage(img); - this.setDisplayName(imgName); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/hard-drive-icon.jpg"); //NON-NLS - - // Listen for ingest events so that we can detect new added files (e.g. carved) - IngestManager.getInstance().addIngestModuleEventListener(pcl); - // Listen for case events so that we can detect when case is closed - Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); - } - - private void removeListeners() { - IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); - } - - /** - * Right click action for this node - * - * @param context - * - * @return - */ - @Override - @Messages({"ImageNode.action.runIngestMods.text=Run Ingest Modules", - "ImageNode.getActions.openFileSearchByAttr.text=Open File Search by Attributes"}) - public Action[] getActions(boolean context) { - - List actionsList = new ArrayList<>(); - for (Action a : super.getActions(true)) { - actionsList.add(a); - } - actionsList.addAll(ExplorerNodeActionVisitor.getActions(content)); - 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)); - if (checkSchemaVersion()) { - actionsList.add(new DeleteDataSourceAction(content.getId())); - } - return actionsList.toArray(new Action[0]); - } - - @Override - @Messages({"ImageNode.createSheet.size.name=Size (Bytes)", - "ImageNode.createSheet.size.displayName=Size (Bytes)", - "ImageNode.createSheet.size.desc=Size of the data source in bytes.", - "ImageNode.createSheet.type.name=Type", - "ImageNode.createSheet.type.displayName=Type", - "ImageNode.createSheet.type.desc=Type of the image.", - "ImageNode.createSheet.type.text=Image", - "ImageNode.createSheet.sectorSize.name=Sector Size (Bytes)", - "ImageNode.createSheet.sectorSize.displayName=Sector Size (Bytes)", - "ImageNode.createSheet.sectorSize.desc=Sector size of the image in bytes.", - "ImageNode.createSheet.timezone.name=Timezone", - "ImageNode.createSheet.timezone.displayName=Timezone", - "ImageNode.createSheet.timezone.desc=Timezone of the image", - "ImageNode.createSheet.deviceId.name=Device ID", - "ImageNode.createSheet.deviceId.displayName=Device ID", - "ImageNode.createSheet.deviceId.desc=Device ID of the image"}) - protected Sheet createSheet() { - Sheet sheet = super.createSheet(); - Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); - if (sheetSet == null) { - sheetSet = Sheet.createPropertiesSet(); - sheet.put(sheetSet); - } - - sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ImageNode.createSheet.name.name"), - NbBundle.getMessage(this.getClass(), "ImageNode.createSheet.name.displayName"), - NbBundle.getMessage(this.getClass(), "ImageNode.createSheet.name.desc"), - getDisplayName())); - - sheetSet.put(new NodeProperty<>(Bundle.ImageNode_createSheet_type_name(), - Bundle.ImageNode_createSheet_type_displayName(), - Bundle.ImageNode_createSheet_type_desc(), - Bundle.ImageNode_createSheet_type_text())); - - sheetSet.put(new NodeProperty<>(Bundle.ImageNode_createSheet_size_name(), - Bundle.ImageNode_createSheet_size_displayName(), - Bundle.ImageNode_createSheet_size_desc(), - this.content.getSize())); - sheetSet.put(new NodeProperty<>(Bundle.ImageNode_createSheet_sectorSize_name(), - Bundle.ImageNode_createSheet_sectorSize_displayName(), - Bundle.ImageNode_createSheet_sectorSize_desc(), - this.content.getSsize())); - - sheetSet.put(new NodeProperty<>(Bundle.ImageNode_createSheet_timezone_name(), - Bundle.ImageNode_createSheet_timezone_displayName(), - Bundle.ImageNode_createSheet_timezone_desc(), - this.content.getTimeZone())); - - try (CaseDbQuery query = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery("SELECT device_id FROM data_source_info WHERE obj_id = " + this.content.getId());) { - ResultSet deviceIdSet = query.getResultSet(); - if (deviceIdSet.next()) { - sheetSet.put(new NodeProperty<>(Bundle.ImageNode_createSheet_deviceId_name(), - Bundle.ImageNode_createSheet_deviceId_displayName(), - Bundle.ImageNode_createSheet_deviceId_desc(), - deviceIdSet.getString("device_id"))); - } - } catch (SQLException | TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Failed to get device id for the following image: " + this.content.getId(), ex); - } - - return sheet; - } - - @Override - public T accept(ContentNodeVisitor visitor) { - return visitor.visit(this); - } - - @Override - public boolean isLeafTypeNode() { - return false; - } - - @Override - public T accept(DisplayableItemNodeVisitor visitor) { - return visitor.visit(this); - } - - @Override - public String getItemType() { - 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; - - } - - /* - * 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 - * and was ingested by the RawDSProcessor). - */ - private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { - String eventType = evt.getPropertyName(); - - // See if the new file is a child of ours - if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) { - if ((evt.getOldValue() instanceof ModuleContentEvent) == false) { - return; - } - ModuleContentEvent moduleContentEvent = (ModuleContentEvent) evt.getOldValue(); - if ((moduleContentEvent.getSource() instanceof Content) == false) { - return; - } - Content newContent = (Content) moduleContentEvent.getSource(); - - try { - Content parent = newContent.getParent(); - if (parent != null) { - // Is this a new carved file? - if (parent.getName().equals(VirtualDirectory.NAME_CARVED)) { - // Is this new carved file for this data source? - if (newContent.getDataSource().getId() == getContent().getDataSource().getId()) { - // Find the image (if any) associated with the new content and - // trigger a refresh if it matches the image wrapped by this node. - while ((parent = parent.getParent()) != null) { - if (parent.getId() == getContent().getId()) { - BaseChildFactory.post(getName(), new BaseChildFactory.RefreshKeysEvent()); - break; - } - } - } - } - } - } catch (TskCoreException ex) { - // Do nothing. - } catch (NoSuchEventBusException ex) { - logger.log(Level.WARNING, "Failed to post key refresh event.", ex); // NON-NLS - } - } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { - if (evt.getNewValue() == null) { - // case was closed. Remove listeners so that we don't get called with a stale case handle - removeListeners(); - } - } - }; - - /** - * Reads and returns a list of all tags associated with this content node. - * - * Null implementation of an abstract method. - * - * @return list of tags associated with the node. - */ - @Override - protected List getAllTagsFromDatabase() { - return new ArrayList<>(); - } - - /** - * Returns correlation attribute instance for the underlying content of the - * node. - * - * Null implementation of an abstract method. - * - * @return correlation attribute instance for the underlying content of the - * node. - */ - @Override - protected CorrelationAttributeInstance getCorrelationAttributeInstance() { - return null; - } - - /** - * Returns Score property for the node. - * - * Null implementation of an abstract method. - * - * @param tags list of tags. - * - * @return Score property for the underlying content of the node. - */ - @Override - protected Pair getScorePropertyAndDescription(List tags) { - return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR); - } - - /** - * Returns comment property for the node. - * - * Null implementation of an abstract method. - * - * @param tags list of tags - * @param attribute correlation attribute instance - * - * @return Comment property for the underlying content of the node. - */ - @Override - protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { - return DataResultViewerTable.HasCommentStatus.NO_COMMENT; - } - - /** - * Returns occurrences/count property for the node. - * - * Null implementation of an abstract method. - * - * @param attributeType the type of the attribute to count - * @param attributeValue the value of the attribute to coun - * @param defaultDescription a description to use when none is determined by - * the getCountPropertyAndDescription method - * - * @return count property for the underlying content of the node. - */ - @Override - protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) { - return Pair.of(-1L, NO_DESCR); - } -} +/* + * Autopsy Forensic Browser + * + * Copyright 2011-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.datamodel; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.logging.Level; +import javax.swing.Action; +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.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.directorytree.ExplorerNodeActionVisitor; +import org.sleuthkit.autopsy.directorytree.FileSearchAction; +import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.ingest.ModuleContentEvent; +import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.RunIngestModulesAction; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.Image; +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; + +/** + * This class is used to represent the "Node" for the image. The children of + * this node are volumes. + */ +public class ImageNode extends AbstractContentNode { + + private static final Logger logger = Logger.getLogger(ImageNode.class.getName()); + + /** + * Helper so that the display name and the name used in building the path + * are determined the same way. + * + * @param i Image to get the name of + * + * @return short name for the Image + */ + static String nameForImage(Image i) { + return i.getName(); + } + + /** + * @param img + */ + public ImageNode(Image img) { + super(img); + + // set name, display name, and icon + String imgName = nameForImage(img); + this.setDisplayName(imgName); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/hard-drive-icon.jpg"); //NON-NLS + + // Listen for ingest events so that we can detect new added files (e.g. carved) + IngestManager.getInstance().addIngestModuleEventListener(pcl); + // Listen for case events so that we can detect when case is closed + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); + } + + private void removeListeners() { + IngestManager.getInstance().removeIngestModuleEventListener(pcl); + Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); + } + + /** + * Right click action for this node + * + * @param context + * + * @return + */ + @Override + @Messages({"ImageNode.action.runIngestMods.text=Run Ingest Modules", + "ImageNode.getActions.openFileSearchByAttr.text=Open File Search by Attributes"}) + public Action[] getActions(boolean context) { + + List actionsList = new ArrayList<>(); + for (Action a : super.getActions(true)) { + actionsList.add(a); + } + actionsList.addAll(ExplorerNodeActionVisitor.getActions(content)); + 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)); + if (checkSchemaVersion()) { + actionsList.add(new DeleteDataSourceAction(content.getId())); + } + return actionsList.toArray(new Action[0]); + } + + @Override + @Messages({"ImageNode.createSheet.size.name=Size (Bytes)", + "ImageNode.createSheet.size.displayName=Size (Bytes)", + "ImageNode.createSheet.size.desc=Size of the data source in bytes.", + "ImageNode.createSheet.type.name=Type", + "ImageNode.createSheet.type.displayName=Type", + "ImageNode.createSheet.type.desc=Type of the image.", + "ImageNode.createSheet.type.text=Image", + "ImageNode.createSheet.sectorSize.name=Sector Size (Bytes)", + "ImageNode.createSheet.sectorSize.displayName=Sector Size (Bytes)", + "ImageNode.createSheet.sectorSize.desc=Sector size of the image in bytes.", + "ImageNode.createSheet.timezone.name=Timezone", + "ImageNode.createSheet.timezone.displayName=Timezone", + "ImageNode.createSheet.timezone.desc=Timezone of the image", + "ImageNode.createSheet.deviceId.name=Device ID", + "ImageNode.createSheet.deviceId.displayName=Device ID", + "ImageNode.createSheet.deviceId.desc=Device ID of the image"}) + protected Sheet createSheet() { + Sheet sheet = super.createSheet(); + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); + if (sheetSet == null) { + sheetSet = Sheet.createPropertiesSet(); + sheet.put(sheetSet); + } + + sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ImageNode.createSheet.name.name"), + NbBundle.getMessage(this.getClass(), "ImageNode.createSheet.name.displayName"), + NbBundle.getMessage(this.getClass(), "ImageNode.createSheet.name.desc"), + getDisplayName())); + + sheetSet.put(new NodeProperty<>(Bundle.ImageNode_createSheet_type_name(), + Bundle.ImageNode_createSheet_type_displayName(), + Bundle.ImageNode_createSheet_type_desc(), + Bundle.ImageNode_createSheet_type_text())); + + sheetSet.put(new NodeProperty<>(Bundle.ImageNode_createSheet_size_name(), + Bundle.ImageNode_createSheet_size_displayName(), + Bundle.ImageNode_createSheet_size_desc(), + this.content.getSize())); + sheetSet.put(new NodeProperty<>(Bundle.ImageNode_createSheet_sectorSize_name(), + Bundle.ImageNode_createSheet_sectorSize_displayName(), + Bundle.ImageNode_createSheet_sectorSize_desc(), + this.content.getSsize())); + + sheetSet.put(new NodeProperty<>(Bundle.ImageNode_createSheet_timezone_name(), + Bundle.ImageNode_createSheet_timezone_displayName(), + Bundle.ImageNode_createSheet_timezone_desc(), + this.content.getTimeZone())); + + try (CaseDbQuery query = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery("SELECT device_id FROM data_source_info WHERE obj_id = " + this.content.getId());) { + ResultSet deviceIdSet = query.getResultSet(); + if (deviceIdSet.next()) { + sheetSet.put(new NodeProperty<>(Bundle.ImageNode_createSheet_deviceId_name(), + Bundle.ImageNode_createSheet_deviceId_displayName(), + Bundle.ImageNode_createSheet_deviceId_desc(), + deviceIdSet.getString("device_id"))); + } + } catch (SQLException | TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Failed to get device id for the following image: " + this.content.getId(), ex); + } + + return sheet; + } + + @Override + public T accept(ContentNodeVisitor visitor) { + return visitor.visit(this); + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + public T accept(DisplayableItemNodeVisitor visitor) { + return visitor.visit(this); + } + + @Override + public String getItemType() { + 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; + + } + + /* + * 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 + * and was ingested by the RawDSProcessor). + */ + private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { + String eventType = evt.getPropertyName(); + + // See if the new file is a child of ours + if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) { + if ((evt.getOldValue() instanceof ModuleContentEvent) == false) { + return; + } + ModuleContentEvent moduleContentEvent = (ModuleContentEvent) evt.getOldValue(); + if ((moduleContentEvent.getSource() instanceof Content) == false) { + return; + } + Content newContent = (Content) moduleContentEvent.getSource(); + + try { + Content parent = newContent.getParent(); + if (parent != null) { + // Is this a new carved file? + if (parent.getName().equals(VirtualDirectory.NAME_CARVED)) { + // Is this new carved file for this data source? + if (newContent.getDataSource().getId() == getContent().getDataSource().getId()) { + // Find the image (if any) associated with the new content and + // trigger a refresh if it matches the image wrapped by this node. + while ((parent = parent.getParent()) != null) { + if (parent.getId() == getContent().getId()) { + BaseChildFactory.post(getName(), new BaseChildFactory.RefreshKeysEvent()); + break; + } + } + } + } + } + } catch (TskCoreException ex) { + // Do nothing. + } catch (NoSuchEventBusException ex) { + logger.log(Level.WARNING, "Failed to post key refresh event.", ex); // NON-NLS + } + } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + if (evt.getNewValue() == null) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + removeListeners(); + } + } + }; + + /** + * Reads and returns a list of all tags associated with this content node. + * + * Null implementation of an abstract method. + * + * @return list of tags associated with the node. + */ + @Override + protected List getAllTagsFromDatabase() { + return new ArrayList<>(); + } + + /** + * Returns correlation attribute instance for the underlying content of the + * node. + * + * Null implementation of an abstract method. + * + * @return correlation attribute instance for the underlying content of the + * node. + */ + @Override + protected CorrelationAttributeInstance getCorrelationAttributeInstance() { + return null; + } + + /** + * Returns Score property for the node. + * + * Null implementation of an abstract method. + * + * @param tags list of tags. + * + * @return Score property for the underlying content of the node. + */ + @Override + protected Pair getScorePropertyAndDescription(List tags) { + return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR); + } + + /** + * Returns comment property for the node. + * + * Null implementation of an abstract method. + * + * @param tags list of tags + * @param attribute correlation attribute instance + * + * @return Comment property for the underlying content of the node. + */ + @Override + protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { + return DataResultViewerTable.HasCommentStatus.NO_COMMENT; + } + + /** + * Returns occurrences/count property for the node. + * + * Null implementation of an abstract method. + * + * @param attributeType the type of the attribute to count + * @param attributeValue the value of the attribute to coun + * @param defaultDescription a description to use when none is determined by + * the getCountPropertyAndDescription method + * + * @return count property for the underlying content of the node. + */ + @Override + protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) { + return Pair.of(-1L, NO_DESCR); + } +} diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java index 4155708a28..ce72c2cf69 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java @@ -1,364 +1,364 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011-2018 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.keywordsearch; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import javax.swing.SwingWorker; -import org.apache.commons.lang.StringUtils; -import org.netbeans.api.progress.ProgressHandle; -import org.netbeans.api.progress.aggregate.ProgressContributor; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.coreutils.EscapeUtil; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.ingest.IngestMessage; -import org.sleuthkit.autopsy.ingest.IngestServices;; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.Blackboard; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Stores and processes the results of a keyword search query. Processing - * includes posting keyword hit artifacts to the blackboard, sending messages - * about the search hits to the ingest inbox, and publishing an event to notify - * subscribers of the blackboard posts. - */ -class QueryResults { - - private static final Logger logger = Logger.getLogger(QueryResults.class.getName()); - private static final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); - private final KeywordSearchQuery query; - private final Map> results = new HashMap<>(); - - /** - * Constructs a object that stores and processes the results of a keyword - * search query. Processing includes adding keyword hit artifacts to the - * blackboard, sending messages about the search hits to the ingest inbox, - * and publishing an event to notify subscribers of the blackboard posts. - * - * The KeywordSearchQuery is used to do the blackboard posts. - * - * @param query The query. - */ - QueryResults(KeywordSearchQuery query) { - this.query = query; - } - - /** - * Gets the keyword search query that generated the results stored in this - * object. - * - * @return The query. - */ - KeywordSearchQuery getQuery() { - return query; - } - - /** - * Adds the keyword hits for a keyword to the hits that are stored in this - * object. All calls to this method MUST be completed before calling the - * process method. - * - * @param keyword The keyword, - * @param hits The hits. - */ - void addResult(Keyword keyword, List hits) { - results.put(keyword, hits); - } - - /** - * Gets the keyword hits stored in this object for a given keyword. - * - * @param keyword The keyword. - * - * @return The keyword hits. - */ - List getResults(Keyword keyword) { - return results.get(keyword); - } - - /** - * Gets the set of unique keywords for which keyword hits have been stored - * in this object. - * - * @return - */ - Set getKeywords() { - return results.keySet(); - } - - /** - * Processes the keyword hits stored in this object by adding keyword hit - * artifacts to the blackboard, sending messages about the search hits to - * the ingest inbox, and publishing an event to notify subscribers of the - * blackboard posts. - * - * Makes one artifact per keyword per searched text source object (file or - * artifact), i.e., if a keyword is found several times in the text - * extracted from the source object, only one artifact is created. - * - * This method ASSUMES that the processing is being done using a SwingWorker - * that should be checked for task cancellation. - * - * All calls to the addResult method MUST be completed before calling this - * method. - * - * @param progress A progress indicator that reports the number of - * keywords processed. Can be null. - * @param subProgress A progress contributor that reports the keyword - * currently being processed. Can be null. - * @param worker The SwingWorker that is being used to do the - * processing, will be checked for task cancellation - * before processing each keyword. - * @param notifyInbox Whether or not to write a message to the ingest - * messages inbox if there is a keyword hit in the text - * exrtacted from the text source object. - * @param saveResults Flag whether to save search results as KWS artifacts. - * - */ - void process(ProgressHandle progress, ProgressContributor subProgress, SwingWorker worker, boolean notifyInbox, boolean saveResults) { - /* - * Initialize the progress indicator to the number of keywords that will - * be processed. - */ - if (null != progress) { - progress.start(getKeywords().size()); - } - - /* - * Process the keyword hits for each keyword. - */ - int keywordsProcessed = 0; - final Collection hitArtifacts = new ArrayList<>(); - for (final Keyword keyword : getKeywords()) { - /* - * Cancellation check. - */ - if (worker.isCancelled()) { - logger.log(Level.INFO, "Processing cancelled, exiting before processing search term {0}", keyword.getSearchTerm()); //NON-NLS - break; - } - - /* - * Update the progress indicator and the show the current keyword - * via the progress contributor. - */ - if (progress != null) { - progress.progress(keyword.toString(), keywordsProcessed); - } - if (subProgress != null) { - String hitDisplayStr = keyword.getSearchTerm(); - if (hitDisplayStr.length() > 50) { - hitDisplayStr = hitDisplayStr.substring(0, 49) + "..."; - } - subProgress.progress(query.getKeywordList().getName() + ": " + hitDisplayStr, keywordsProcessed); - } - - /* - * Reduce the hits for this keyword to one hit per text source - * object so that only one hit artifact is generated per text source - * object, no matter how many times the keyword was actually found. - */ - for (KeywordHit hit : getOneHitPerTextSourceObject(keyword)) { - /* - * Get a snippet (preview) for the hit. Regex queries always - * have snippets made from the content_str pulled back from Solr - * for executing the search. Other types of queries may or may - * not have snippets yet. - */ - String snippet = hit.getSnippet(); - if (StringUtils.isBlank(snippet)) { - final String snippetQuery = KeywordSearchUtil.escapeLuceneQuery(keyword.getSearchTerm()); - try { - snippet = LuceneQuery.querySnippet(snippetQuery, hit.getSolrObjectId(), hit.getChunkId(), !query.isLiteral(), true); - } catch (NoOpenCoreException e) { - logger.log(Level.SEVERE, "Solr core closed while executing snippet query " + snippetQuery, e); //NON-NLS - break; // Stop processing. - } catch (Exception e) { - logger.log(Level.SEVERE, "Error executing snippet query " + snippetQuery, e); //NON-NLS - continue; // Try processing the next hit. - } - } - - /* - * Get the content (file or artifact) that is the text source - * for the hit. - */ - Content content = null; - try { - SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - content = tskCase.getContentById(hit.getContentID()); - } catch (TskCoreException | NoCurrentCaseException tskCoreException) { - logger.log(Level.SEVERE, "Failed to get text source object for keyword hit", tskCoreException); //NON-NLS - } - - if ((content != null) && saveResults) { - /* - * Post an artifact for the hit to the blackboard. - */ - BlackboardArtifact artifact = query.createKeywordHitArtifact(content, keyword, hit, snippet, query.getKeywordList().getName()); - - /* - * Send an ingest inbox message for the hit. - */ - if (null != artifact) { - hitArtifacts.add(artifact); - if (notifyInbox) { - try { - writeSingleFileInboxMessage(artifact, content); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error sending message to ingest messages inbox", ex); //NON-NLS - } - } - } - } - } - - ++keywordsProcessed; - } - - /* - * Post the artifacts to the blackboard which will publish an event to - * notify subscribers of the new artifacts. - */ - if (!hitArtifacts.isEmpty()) { - try { - SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - Blackboard blackboard = tskCase.getBlackboard(); - - blackboard.postArtifacts(hitArtifacts, MODULE_NAME); - } catch (NoCurrentCaseException | Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, "Failed to post KWH artifact to blackboard.", ex); //NON-NLS - } - } - } - - /** - * Reduce the hits for a given keyword to one hit per text source object so - * that only one hit artifact is generated per text source object, no matter - * how many times the keyword was actually found. - * - * @param keyword The keyword. - * - * @return Collection The reduced set of keyword hits. - */ - private Collection getOneHitPerTextSourceObject(Keyword keyword) { - /* - * For each Solr document (chunk) for a text source object, return only - * a single keyword hit from the first chunk of text (the one with the - * lowest chunk id). - */ - HashMap< Long, KeywordHit> hits = new HashMap<>(); - getResults(keyword).forEach((hit) -> { - if (!hits.containsKey(hit.getSolrObjectId())) { - hits.put(hit.getSolrObjectId(), hit); - } else if (hit.getChunkId() < hits.get(hit.getSolrObjectId()).getChunkId()) { - hits.put(hit.getSolrObjectId(), hit); - } - }); - return hits.values(); - } - - /** - * Send an ingest inbox message indicating that there was a keyword hit in - * the given text source object. - * - * @param artifact The keyword hit artifact for the hit. - * @param hitContent The text source object. - * - * @throws TskCoreException If there is a problem generating or send the - * inbox message. - */ - private void writeSingleFileInboxMessage(BlackboardArtifact artifact, Content hitContent) throws TskCoreException { - StringBuilder subjectSb = new StringBuilder(1024); - if (!query.isLiteral()) { - subjectSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.regExpHitLbl")); - } else { - subjectSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitLbl")); - } - - StringBuilder detailsSb = new StringBuilder(1024); - String uniqueKey = null; - BlackboardAttribute attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD)); - if (attr != null) { - final String keyword = attr.getValueString(); - subjectSb.append(keyword); - uniqueKey = keyword.toLowerCase(); - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitThLbl")); - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - } - - //preview - attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW)); - if (attr != null) { - detailsSb.append(""); //NON-NLS - detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.previewThLbl")); - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - } - - //file - detailsSb.append(""); //NON-NLS - detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.fileThLbl")); - if (hitContent instanceof AbstractFile) { - AbstractFile hitFile = (AbstractFile) hitContent; - detailsSb.append(""); //NON-NLS - } else { - detailsSb.append(""); //NON-NLS - } - detailsSb.append(""); //NON-NLS - - //list - attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME)); - if (attr != null) { - detailsSb.append(""); //NON-NLS - detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.listThLbl")); - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - } - - //regex - if (!query.isLiteral()) { - attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP)); - if (attr != null) { - detailsSb.append(""); //NON-NLS - detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.regExThLbl")); - detailsSb.append(""); //NON-NLS - detailsSb.append(""); //NON-NLS - } - } - detailsSb.append("
").append(EscapeUtil.escapeHtml(keyword)).append("
").append(EscapeUtil.escapeHtml(attr.getValueString())).append("
").append(hitFile.getParentPath()).append(hitFile.getName()).append("").append(hitContent.getName()).append("
").append(attr.getValueString()).append("
").append(attr.getValueString()).append("
"); //NON-NLS - - IngestServices.getInstance().postMessage(IngestMessage.createDataMessage(MODULE_NAME, subjectSb.toString(), detailsSb.toString(), uniqueKey, artifact)); - } -} +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2018 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.keywordsearch; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import javax.swing.SwingWorker; +import org.apache.commons.lang.StringUtils; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.aggregate.ProgressContributor; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.coreutils.EscapeUtil; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.IngestMessage; +import org.sleuthkit.autopsy.ingest.IngestServices;; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Blackboard; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Stores and processes the results of a keyword search query. Processing + * includes posting keyword hit artifacts to the blackboard, sending messages + * about the search hits to the ingest inbox, and publishing an event to notify + * subscribers of the blackboard posts. + */ +class QueryResults { + + private static final Logger logger = Logger.getLogger(QueryResults.class.getName()); + private static final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); + private final KeywordSearchQuery query; + private final Map> results = new HashMap<>(); + + /** + * Constructs a object that stores and processes the results of a keyword + * search query. Processing includes adding keyword hit artifacts to the + * blackboard, sending messages about the search hits to the ingest inbox, + * and publishing an event to notify subscribers of the blackboard posts. + * + * The KeywordSearchQuery is used to do the blackboard posts. + * + * @param query The query. + */ + QueryResults(KeywordSearchQuery query) { + this.query = query; + } + + /** + * Gets the keyword search query that generated the results stored in this + * object. + * + * @return The query. + */ + KeywordSearchQuery getQuery() { + return query; + } + + /** + * Adds the keyword hits for a keyword to the hits that are stored in this + * object. All calls to this method MUST be completed before calling the + * process method. + * + * @param keyword The keyword, + * @param hits The hits. + */ + void addResult(Keyword keyword, List hits) { + results.put(keyword, hits); + } + + /** + * Gets the keyword hits stored in this object for a given keyword. + * + * @param keyword The keyword. + * + * @return The keyword hits. + */ + List getResults(Keyword keyword) { + return results.get(keyword); + } + + /** + * Gets the set of unique keywords for which keyword hits have been stored + * in this object. + * + * @return + */ + Set getKeywords() { + return results.keySet(); + } + + /** + * Processes the keyword hits stored in this object by adding keyword hit + * artifacts to the blackboard, sending messages about the search hits to + * the ingest inbox, and publishing an event to notify subscribers of the + * blackboard posts. + * + * Makes one artifact per keyword per searched text source object (file or + * artifact), i.e., if a keyword is found several times in the text + * extracted from the source object, only one artifact is created. + * + * This method ASSUMES that the processing is being done using a SwingWorker + * that should be checked for task cancellation. + * + * All calls to the addResult method MUST be completed before calling this + * method. + * + * @param progress A progress indicator that reports the number of + * keywords processed. Can be null. + * @param subProgress A progress contributor that reports the keyword + * currently being processed. Can be null. + * @param worker The SwingWorker that is being used to do the + * processing, will be checked for task cancellation + * before processing each keyword. + * @param notifyInbox Whether or not to write a message to the ingest + * messages inbox if there is a keyword hit in the text + * exrtacted from the text source object. + * @param saveResults Flag whether to save search results as KWS artifacts. + * + */ + void process(ProgressHandle progress, ProgressContributor subProgress, SwingWorker worker, boolean notifyInbox, boolean saveResults) { + /* + * Initialize the progress indicator to the number of keywords that will + * be processed. + */ + if (null != progress) { + progress.start(getKeywords().size()); + } + + /* + * Process the keyword hits for each keyword. + */ + int keywordsProcessed = 0; + final Collection hitArtifacts = new ArrayList<>(); + for (final Keyword keyword : getKeywords()) { + /* + * Cancellation check. + */ + if (worker.isCancelled()) { + logger.log(Level.INFO, "Processing cancelled, exiting before processing search term {0}", keyword.getSearchTerm()); //NON-NLS + break; + } + + /* + * Update the progress indicator and the show the current keyword + * via the progress contributor. + */ + if (progress != null) { + progress.progress(keyword.toString(), keywordsProcessed); + } + if (subProgress != null) { + String hitDisplayStr = keyword.getSearchTerm(); + if (hitDisplayStr.length() > 50) { + hitDisplayStr = hitDisplayStr.substring(0, 49) + "..."; + } + subProgress.progress(query.getKeywordList().getName() + ": " + hitDisplayStr, keywordsProcessed); + } + + /* + * Reduce the hits for this keyword to one hit per text source + * object so that only one hit artifact is generated per text source + * object, no matter how many times the keyword was actually found. + */ + for (KeywordHit hit : getOneHitPerTextSourceObject(keyword)) { + /* + * Get a snippet (preview) for the hit. Regex queries always + * have snippets made from the content_str pulled back from Solr + * for executing the search. Other types of queries may or may + * not have snippets yet. + */ + String snippet = hit.getSnippet(); + if (StringUtils.isBlank(snippet)) { + final String snippetQuery = KeywordSearchUtil.escapeLuceneQuery(keyword.getSearchTerm()); + try { + snippet = LuceneQuery.querySnippet(snippetQuery, hit.getSolrObjectId(), hit.getChunkId(), !query.isLiteral(), true); + } catch (NoOpenCoreException e) { + logger.log(Level.SEVERE, "Solr core closed while executing snippet query " + snippetQuery, e); //NON-NLS + break; // Stop processing. + } catch (Exception e) { + logger.log(Level.SEVERE, "Error executing snippet query " + snippetQuery, e); //NON-NLS + continue; // Try processing the next hit. + } + } + + /* + * Get the content (file or artifact) that is the text source + * for the hit. + */ + Content content = null; + try { + SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + content = tskCase.getContentById(hit.getContentID()); + } catch (TskCoreException | NoCurrentCaseException tskCoreException) { + logger.log(Level.SEVERE, "Failed to get text source object for keyword hit", tskCoreException); //NON-NLS + } + + if ((content != null) && saveResults) { + /* + * Post an artifact for the hit to the blackboard. + */ + BlackboardArtifact artifact = query.createKeywordHitArtifact(content, keyword, hit, snippet, query.getKeywordList().getName()); + + /* + * Send an ingest inbox message for the hit. + */ + if (null != artifact) { + hitArtifacts.add(artifact); + if (notifyInbox) { + try { + writeSingleFileInboxMessage(artifact, content); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error sending message to ingest messages inbox", ex); //NON-NLS + } + } + } + } + } + + ++keywordsProcessed; + } + + /* + * Post the artifacts to the blackboard which will publish an event to + * notify subscribers of the new artifacts. + */ + if (!hitArtifacts.isEmpty()) { + try { + SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + Blackboard blackboard = tskCase.getBlackboard(); + + blackboard.postArtifacts(hitArtifacts, MODULE_NAME); + } catch (NoCurrentCaseException | Blackboard.BlackboardException ex) { + logger.log(Level.SEVERE, "Failed to post KWH artifact to blackboard.", ex); //NON-NLS + } + } + } + + /** + * Reduce the hits for a given keyword to one hit per text source object so + * that only one hit artifact is generated per text source object, no matter + * how many times the keyword was actually found. + * + * @param keyword The keyword. + * + * @return Collection The reduced set of keyword hits. + */ + private Collection getOneHitPerTextSourceObject(Keyword keyword) { + /* + * For each Solr document (chunk) for a text source object, return only + * a single keyword hit from the first chunk of text (the one with the + * lowest chunk id). + */ + HashMap< Long, KeywordHit> hits = new HashMap<>(); + getResults(keyword).forEach((hit) -> { + if (!hits.containsKey(hit.getSolrObjectId())) { + hits.put(hit.getSolrObjectId(), hit); + } else if (hit.getChunkId() < hits.get(hit.getSolrObjectId()).getChunkId()) { + hits.put(hit.getSolrObjectId(), hit); + } + }); + return hits.values(); + } + + /** + * Send an ingest inbox message indicating that there was a keyword hit in + * the given text source object. + * + * @param artifact The keyword hit artifact for the hit. + * @param hitContent The text source object. + * + * @throws TskCoreException If there is a problem generating or send the + * inbox message. + */ + private void writeSingleFileInboxMessage(BlackboardArtifact artifact, Content hitContent) throws TskCoreException { + StringBuilder subjectSb = new StringBuilder(1024); + if (!query.isLiteral()) { + subjectSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.regExpHitLbl")); + } else { + subjectSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitLbl")); + } + + StringBuilder detailsSb = new StringBuilder(1024); + String uniqueKey = null; + BlackboardAttribute attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD)); + if (attr != null) { + final String keyword = attr.getValueString(); + subjectSb.append(keyword); + uniqueKey = keyword.toLowerCase(); + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitThLbl")); + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + } + + //preview + attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW)); + if (attr != null) { + detailsSb.append(""); //NON-NLS + detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.previewThLbl")); + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + } + + //file + detailsSb.append(""); //NON-NLS + detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.fileThLbl")); + if (hitContent instanceof AbstractFile) { + AbstractFile hitFile = (AbstractFile) hitContent; + detailsSb.append(""); //NON-NLS + } else { + detailsSb.append(""); //NON-NLS + } + detailsSb.append(""); //NON-NLS + + //list + attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME)); + if (attr != null) { + detailsSb.append(""); //NON-NLS + detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.listThLbl")); + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + } + + //regex + if (!query.isLiteral()) { + attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP)); + if (attr != null) { + detailsSb.append(""); //NON-NLS + detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.regExThLbl")); + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + } + } + detailsSb.append("
").append(EscapeUtil.escapeHtml(keyword)).append("
").append(EscapeUtil.escapeHtml(attr.getValueString())).append("
").append(hitFile.getParentPath()).append(hitFile.getName()).append("").append(hitContent.getName()).append("
").append(attr.getValueString()).append("
").append(attr.getValueString()).append("
"); //NON-NLS + + IngestServices.getInstance().postMessage(IngestMessage.createDataMessage(MODULE_NAME, subjectSb.toString(), detailsSb.toString(), uniqueKey, artifact)); + } +} From 9cd72eb1c005e3f6ded84f10cfad66544902be46 Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 9 Oct 2019 11:46:23 -0400 Subject: [PATCH 32/81] Remove tests from Travis build. --- .travis.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7554bbc6a4..56c8b7bbbd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,10 +64,3 @@ script: - cd $TRAVIS_BUILD_DIR/ - ant build - echo -en 'travis_fold:end:script.build\\r' - - echo "Testing Autopsy..." && echo -en 'travis_fold:start:script.tests\\r' - - cd Core/ - - ant -q getTestDataFiles - - echo "Free Space:" - - echo `df -h .` - - xvfb-run ant -q test - - echo -en 'travis_fold:end:script.tests\\r' From 761c715a8932ccb3c4eb37c1986a226f388ad35e Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 9 Oct 2019 11:48:30 -0400 Subject: [PATCH 33/81] Remove tests from AppVeyor build. --- appveyor.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 03eaf5a4ae..d7cba33584 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -45,8 +45,5 @@ build_script: - ps: popd - cd %APPVEYOR_BUILD_FOLDER% - cmd: ant -q build - - cd Core - - cmd: ant -q test - - cd .. test: off From d9f4e47b9b4fc2a26dad2482aeb5d4352fb09418 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Wed, 9 Oct 2019 17:20:11 -0400 Subject: [PATCH 34/81] Update KeywordSearchService.java Changed comment in code to reflect reviewers comment --- .../autopsy/keywordsearchservice/KeywordSearchService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java b/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java index fcd0d7f2f8..3ef8822e89 100644 --- a/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java +++ b/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java @@ -100,7 +100,7 @@ public interface KeywordSearchService extends Closeable { /** * Deletes the keyword search text for a specific data source. * - * @param dataSourceId The data source id to be deleted from Solr. + * @param dataSourceId The data source id to be deleted. * * @throws KeywordSearchServiceException if unable to delete. */ From d128e7a902b499b282d3d6820bc618ae05f4216f Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Wed, 9 Oct 2019 17:20:46 -0400 Subject: [PATCH 35/81] Update DeleteDataSourceAction.java Changed selectedDataSource to dataSourceId --- .../autopsy/actions/DeleteDataSourceAction.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index 697e37480b..ae57ac3088 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -35,20 +35,20 @@ import org.sleuthkit.datamodel.TskCoreException; */ public final class DeleteDataSourceAction extends AbstractAction { private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName()); - private final Long selectedDataSource; + private final Long dataSourceId; @NbBundle.Messages({"DeleteDataSourceAction.name.text=Delete Data Source"}) - public DeleteDataSourceAction(Long selectedDataSource) { + public DeleteDataSourceAction(Long dataSourceId) { super(Bundle.DeleteDataSourceAction_name_text()); - this.selectedDataSource = selectedDataSource; + this.dataSourceId = dataSourceId; } @Override public void actionPerformed(ActionEvent event) { try { - Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(selectedDataSource); + Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(dataSourceId); } catch (NoCurrentCaseException | TskCoreException e) { - logger.log(Level.WARNING, "Error Deleting Data source " + selectedDataSource, e); + logger.log(Level.WARNING, "Error Deleting Data source " + dataSourceId, e); } } private static void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException { From 86c45f3e49c994973168eb4920f14cbb58c4d8b6 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Thu, 10 Oct 2019 10:03:36 -0400 Subject: [PATCH 36/81] changes based on reviewer comments addressed comments from reviewer. --- .../actions/DeleteDataSourceAction.java | 3 +- .../keywordsearch/Bundle.properties-MERGED | 1 + .../autopsy/keywordsearch/Server.java | 131 +++++++++--------- .../keywordsearch/SolrSearchService.java | 4 +- 4 files changed, 73 insertions(+), 66 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index ae57ac3088..1292e079d4 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -47,7 +47,8 @@ public final class DeleteDataSourceAction extends AbstractAction { public void actionPerformed(ActionEvent event) { try { Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(dataSourceId); - } catch (NoCurrentCaseException | TskCoreException e) { + deleteDataSource(dataSourceId); + } catch (NoCurrentCaseException | TskCoreException | KeywordSearchServiceException e) { logger.log(Level.WARNING, "Error Deleting Data source " + dataSourceId, e); } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED index 7edcb002b2..0c5b0404e8 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED @@ -243,6 +243,7 @@ Server.request.exception.exception.msg=Could not issue Solr request Server.commit.exception.msg=Could not commit index Server.addDoc.exception.msg=Could not add document to index via update handler: {0} Server.addDoc.exception.msg2=Could not add document to index via update handler: {0} +Server.delDoc.exception.msg=Error deleting content from Solr. Solr image id : {0} Server.close.exception.msg=Cannot close Core Server.close.exception.msg2=Cannot close Core Server.solrServerNoPortException.msg=Indexing server could not bind to port {0}, port is not available, consider change the default {1} port. diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index e83acb6b47..d24ebe936b 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -190,16 +190,16 @@ public class Server { } }, /** - * termfreq is a function which returns the number of times the term appears. - * This is not an actual field defined in schema.xml, but can be gotten from returned documents - * in the same way as fields. + * termfreq is a function which returns the number of times the term + * appears. This is not an actual field defined in schema.xml, but can + * be gotten from returned documents in the same way as fields. */ TERMFREQ { @Override public String toString() { return "termfreq"; //NON-NLS } - } + } }; public static final String HL_ANALYZE_CHARS_UNLIMITED = "500000"; //max 1MB in a chunk. use -1 for unlimited, but -1 option may not be supported (not documented) @@ -526,7 +526,7 @@ public class Server { @Override public void run() { MessageNotifyUtil.Notify.error( - NbBundle.getMessage(this.getClass(), "Installer.errorInitKsmMsg"), + NbBundle.getMessage(this.getClass(), "Installer.errorInitKsmMsg"), Bundle.Server_status_failed_msg()); } }); @@ -891,28 +891,30 @@ public class Server { throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.openCore.exception.cantOpen.msg"), ex); } } - + /** - * Get the host and port for a multiuser case. - * If the file solrserver.txt exists, then use the values from that file. - * Otherwise use the settings from the properties file. - * + * Get the host and port for a multiuser case. If the file solrserver.txt + * exists, then use the values from that file. Otherwise use the settings + * from the properties file. + * * @param caseDirectory Current case directory - * @return IndexingServerProperties containing the solr host/port for this case + * + * @return IndexingServerProperties containing the solr host/port for this + * case */ public static IndexingServerProperties getMultiUserServerProperties(String caseDirectory) { Path serverFilePath = Paths.get(caseDirectory, "solrserver.txt"); - if(serverFilePath.toFile().exists()){ - try{ + if (serverFilePath.toFile().exists()) { + try { List lines = Files.readAllLines(serverFilePath); - if(lines.isEmpty()) { + if (lines.isEmpty()) { logger.log(Level.SEVERE, "solrserver.txt file does not contain any data"); - } else if (! lines.get(0).contains(",")) { + } else if (!lines.get(0).contains(",")) { logger.log(Level.SEVERE, "solrserver.txt file is corrupt - could not read host/port from " + lines.get(0)); } else { String[] parts = lines.get(0).split(","); - if(parts.length != 2) { + if (parts.length != 2) { logger.log(Level.SEVERE, "solrserver.txt file is corrupt - could not read host/port from " + lines.get(0)); } else { return new IndexingServerProperties(parts[0], parts[1]); @@ -922,102 +924,104 @@ public class Server { logger.log(Level.SEVERE, "solrserver.txt file could not be read", ex); } } - + // Default back to the user preferences if the solrserver.txt file was not found or if an error occurred String host = UserPreferences.getIndexingServerHost(); String port = UserPreferences.getIndexingServerPort(); return new IndexingServerProperties(host, port); } - + /** - * Pick a solr server to use for this case and record it in the case directory. - * Looks for a file named "solrServerList.txt" in the root output directory - - * if this does not exist then no server is recorded. - * - * Format of solrServerList.txt: - * (host),(port) - * Ex: 10.1.2.34,8983 - * + * Pick a solr server to use for this case and record it in the case + * directory. Looks for a file named "solrServerList.txt" in the root output + * directory - if this does not exist then no server is recorded. + * + * Format of solrServerList.txt: (host),(port) Ex: 10.1.2.34,8983 + * * @param rootOutputDirectory * @param caseDirectoryPath - * @throws KeywordSearchModuleException + * + * @throws KeywordSearchModuleException */ public static void selectSolrServerForCase(Path rootOutputDirectory, Path caseDirectoryPath) throws KeywordSearchModuleException { // Look for the solr server list file String serverListName = "solrServerList.txt"; Path serverListPath = Paths.get(rootOutputDirectory.toString(), serverListName); - if(serverListPath.toFile().exists()){ - + if (serverListPath.toFile().exists()) { + // Read the list of solr servers List lines; - try{ + try { lines = Files.readAllLines(serverListPath); - } catch (IOException ex){ + } catch (IOException ex) { throw new KeywordSearchModuleException(serverListName + " could not be read", ex); } - + // Remove any lines that don't contain a comma (these are likely just whitespace) for (Iterator iterator = lines.iterator(); iterator.hasNext();) { String line = iterator.next(); - if (! line.contains(",")) { + if (!line.contains(",")) { // Remove the current element from the iterator and the list. iterator.remove(); } } - if(lines.isEmpty()) { + if (lines.isEmpty()) { throw new KeywordSearchModuleException(serverListName + " had no valid server information"); } - + // Choose which server to use int rnd = new Random().nextInt(lines.size()); String[] parts = lines.get(rnd).split(","); - if(parts.length != 2) { + if (parts.length != 2) { throw new KeywordSearchModuleException("Invalid server data: " + lines.get(rnd)); } - + // Split it up just to do a sanity check on the data String host = parts[0]; - String port = parts[1]; - if(host.isEmpty() || port.isEmpty()) { + String port = parts[1]; + if (host.isEmpty() || port.isEmpty()) { throw new KeywordSearchModuleException("Invalid server data: " + lines.get(rnd)); } - + // Write the server data to a file Path serverFile = Paths.get(caseDirectoryPath.toString(), "solrserver.txt"); try { caseDirectoryPath.toFile().mkdirs(); - if (! caseDirectoryPath.toFile().exists()) { + if (!caseDirectoryPath.toFile().exists()) { throw new KeywordSearchModuleException("Case directory " + caseDirectoryPath.toString() + " does not exist"); } Files.write(serverFile, lines.get(rnd).getBytes()); - } catch (IOException ex){ + } catch (IOException ex) { throw new KeywordSearchModuleException(serverFile.toString() + " could not be written", ex); } } } - + /** * Helper class to store the current server properties */ public static class IndexingServerProperties { + private final String host; private final String port; - - IndexingServerProperties (String host, String port) { + + IndexingServerProperties(String host, String port) { this.host = host; this.port = port; } /** * Get the host + * * @return host */ public String getHost() { return host; } - + /** * Get the port + * * @return port */ public String getPort() { @@ -1268,12 +1272,12 @@ public class Server { /** * Delete a data source from SOLR. - * + * * @param dataSourceId to delete - * + * * @throws NoOpenCoreException */ - public void deleteDataSource(Long dataSourceId) throws NoOpenCoreException { + public void deleteDataSource(Long dataSourceId) throws KeywordSearchModuleException, NoOpenCoreException { currentCoreLock.writeLock().lock(); try { if (null == currentCore) { @@ -1281,13 +1285,14 @@ public class Server { } currentCore.deleteDataSource(dataSourceId); currentCore.commit(); - } catch (SolrServerException ex) { - logger.log(Level.SEVERE, "Solr delete data dource failed for data source: " + dataSourceId, ex); //NON-NLS + } catch (SolrServerException | KeywordSearchModuleException ex) { + throw new KeywordSearchModuleException( + NbBundle.getMessage(this.getClass(), "Server.delDoc.exception.msg", dataSourceId), ex); } finally { currentCoreLock.writeLock().unlock(); - } + } } - + /** * Get the text contents of the given file as stored in SOLR. * @@ -1398,10 +1403,10 @@ public class Server { * @throws IOException */ void connectToSolrServer(HttpSolrServer solrServer) throws SolrServerException, IOException { - TimingMetric metric = HealthMonitor.getTimingMetric("Solr: Connectivity check"); + TimingMetric metric = HealthMonitor.getTimingMetric("Solr: Connectivity check"); CoreAdminRequest statusRequest = new CoreAdminRequest(); - statusRequest.setCoreName( null ); - statusRequest.setAction( CoreAdminParams.CoreAdminAction.STATUS ); + statusRequest.setCoreName(null); + statusRequest.setAction(CoreAdminParams.CoreAdminAction.STATUS); statusRequest.setIndexInfoNeeded(false); statusRequest.process(solrServer); HealthMonitor.submitTimingMetric(metric); @@ -1458,7 +1463,7 @@ public class Server { // the server to access a core needs to be built from a URL with the // core in it, and is only good for core-specific operations private final HttpSolrServer solrCore; - + private final int QUERY_TIMEOUT_MILLISECONDS = 86400000; // 24 Hours = 86,400,000 Milliseconds private Core(String name, CaseType caseType, Index index) { @@ -1470,7 +1475,7 @@ public class Server { //TODO test these settings // socket read timeout, make large enough so can index larger files - solrCore.setSoTimeout(QUERY_TIMEOUT_MILLISECONDS); + solrCore.setSoTimeout(QUERY_TIMEOUT_MILLISECONDS); //solrCore.setConnectionTimeout(1000); solrCore.setDefaultMaxConnectionsPerHost(32); solrCore.setMaxTotalConnections(32); @@ -1529,17 +1534,18 @@ public class Server { } } - private void deleteDataSource(Long dsObjId) { + private void deleteDataSource(Long dsObjId) throws KeywordSearchModuleException { String dataSourceId = Long.toString(dsObjId); String deleteQuery = "image_id:" + dataSourceId; try { // Get the first result. UpdateResponse updateResponse = solrCore.deleteByQuery(deleteQuery); } catch (SolrServerException | IOException ex) { - logger.log(Level.SEVERE, "Error deleting content from Solr. Solr image id " + dataSourceId, ex); //NON-NLS + throw new KeywordSearchModuleException( + NbBundle.getMessage(this.getClass(), "Server.delDoc.exception.msg", dataSourceId), ex); //NON-NLS } } - + void addDocument(SolrInputDocument doc) throws KeywordSearchModuleException { try { solrCore.add(doc); @@ -1561,7 +1567,8 @@ public class Server { * @param chunkID Chunk ID of the Solr document * * @return Text from matching Solr document (as String). Null if no - * matching Solr document found or error while getting content from Solr + * matching Solr document found or error while getting content + * from Solr */ private String getSolrContent(long contentID, int chunkID) { final SolrQuery q = new SolrQuery(); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java index b8af966867..804c8557d7 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java @@ -208,9 +208,7 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService { public void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException { try { KeywordSearch.getServer().deleteDataSource(dataSourceId); - } catch (NoOpenCoreException ex) { - logger.log(Level.WARNING, NbBundle.getMessage(SolrSearchService.class, - "SolrSearchService.deleteDataSource.exceptionMessage.noCurrentSolrCore")); + } catch (NoOpenCoreException | KeywordSearchModuleException ex) { throw new KeywordSearchServiceException(NbBundle.getMessage(SolrSearchService.class, "SolrSearchService.deleteDataSource.exceptionMessage.noCurrentSolrCore")); } From bfb1bbe12161401cc24b2c472d082bbaead5f916 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 21 Oct 2019 12:19:04 -0400 Subject: [PATCH 37/81] Changed Exception Handling Changed exception handling --- .../keywordsearch/Bundle.properties-MERGED | 3 +- .../autopsy/keywordsearch/Server.java | 29 ++++++------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED index 0c5b0404e8..c44ceb7556 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED @@ -36,7 +36,7 @@ KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found KeywordSearchResultFactory.query.exception.msg=Could not perform the query OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\nThe module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found. +OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\n\The module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found. OpenIDE-Module-Name=KeywordSearch OptionsCategory_Name_KeywordSearchOptions=Keyword Search OptionsCategory_Keywords_KeywordSearchOptions=Keyword Search @@ -243,7 +243,6 @@ Server.request.exception.exception.msg=Could not issue Solr request Server.commit.exception.msg=Could not commit index Server.addDoc.exception.msg=Could not add document to index via update handler: {0} Server.addDoc.exception.msg2=Could not add document to index via update handler: {0} -Server.delDoc.exception.msg=Error deleting content from Solr. Solr image id : {0} Server.close.exception.msg=Cannot close Core Server.close.exception.msg2=Cannot close Core Server.solrServerNoPortException.msg=Indexing server could not bind to port {0}, port is not available, consider change the default {1} port. diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index d24ebe936b..b886e5eb71 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -1277,20 +1277,14 @@ public class Server { * * @throws NoOpenCoreException */ - public void deleteDataSource(Long dataSourceId) throws KeywordSearchModuleException, NoOpenCoreException { + private void deleteDataSource(Long dataSourceId) throws SolrServerException, KeywordSearchModuleException, NoOpenCoreException, IOException { currentCoreLock.writeLock().lock(); - try { - if (null == currentCore) { - throw new NoOpenCoreException(); - } - currentCore.deleteDataSource(dataSourceId); - currentCore.commit(); - } catch (SolrServerException | KeywordSearchModuleException ex) { - throw new KeywordSearchModuleException( - NbBundle.getMessage(this.getClass(), "Server.delDoc.exception.msg", dataSourceId), ex); - } finally { - currentCoreLock.writeLock().unlock(); + if (null == currentCore) { + throw new NoOpenCoreException(); } + currentCore.deleteDataSource(dataSourceId); + currentCore.commit(); + currentCoreLock.writeLock().unlock(); } /** @@ -1534,16 +1528,11 @@ public class Server { } } - private void deleteDataSource(Long dsObjId) throws KeywordSearchModuleException { + private void deleteDataSource(Long dsObjId) throws SolrServerException, IOException { String dataSourceId = Long.toString(dsObjId); String deleteQuery = "image_id:" + dataSourceId; - try { - // Get the first result. - UpdateResponse updateResponse = solrCore.deleteByQuery(deleteQuery); - } catch (SolrServerException | IOException ex) { - throw new KeywordSearchModuleException( - NbBundle.getMessage(this.getClass(), "Server.delDoc.exception.msg", dataSourceId), ex); //NON-NLS - } + // Get the first result. + UpdateResponse updateResponse = solrCore.deleteByQuery(deleteQuery); } void addDocument(SolrInputDocument doc) throws KeywordSearchModuleException { From e9229a20fc91eaecfd57536b27fc1e9989e2e61c Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 21 Oct 2019 13:19:58 -0400 Subject: [PATCH 38/81] Updare for Exception handling Update for exception handling. --- .../src/org/sleuthkit/autopsy/keywordsearch/Server.java | 2 +- .../org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index b886e5eb71..dae1bae943 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -1277,7 +1277,7 @@ public class Server { * * @throws NoOpenCoreException */ - private void deleteDataSource(Long dataSourceId) throws SolrServerException, KeywordSearchModuleException, NoOpenCoreException, IOException { + public void deleteDataSource(Long dataSourceId) throws SolrServerException, KeywordSearchModuleException, NoOpenCoreException, IOException { currentCoreLock.writeLock().lock(); if (null == currentCore) { throw new NoOpenCoreException(); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java index 804c8557d7..13eb75907f 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java @@ -208,7 +208,7 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService { public void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException { try { KeywordSearch.getServer().deleteDataSource(dataSourceId); - } catch (NoOpenCoreException | KeywordSearchModuleException ex) { + } catch (NoOpenCoreException | KeywordSearchModuleException | SolrServerException | IOException ex) { throw new KeywordSearchServiceException(NbBundle.getMessage(SolrSearchService.class, "SolrSearchService.deleteDataSource.exceptionMessage.noCurrentSolrCore")); } From f461194d920a53570184716cb7abd8b42814fa17 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 22 Oct 2019 14:17:06 -0400 Subject: [PATCH 39/81] Update or exception handling Update for exception handling --- .../KeywordSearchService.java | 12 ++++----- .../autopsy/keywordsearch/Bundle.properties | 1 + .../keywordsearch/Bundle.properties-MERGED | 3 ++- .../autopsy/keywordsearch/Server.java | 23 +++++++++-------- .../keywordsearch/SolrSearchService.java | 25 +++++++++---------- 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java b/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java index 3ef8822e89..981abe05de 100644 --- a/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java +++ b/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java @@ -28,11 +28,11 @@ import org.sleuthkit.datamodel.TskCoreException; /** * An interface for implementations of a keyword search service. You can find * the implementations by using Lookup, such as: - * + * * Lookup.getDefault().lookup(KeywordSearchService.class) - * + * * although most clients should obtain a keyword search service by calling: - * + * * Case.getCurrentCase().getServices().getKeywordSearchService() * * TODO (AUT-2158): This interface should not extend Closeable. @@ -82,7 +82,7 @@ public interface KeywordSearchService extends Closeable { * @throws KeywordSearchServiceException if unable to delete. */ public void deleteTextIndex(CaseMetadata metadata) throws KeywordSearchServiceException; - + /** * Closes the keyword search service. * @@ -95,7 +95,7 @@ public interface KeywordSearchService extends Closeable { * No-op maintained for backwards compatibility. Clients should not * attempt to close case services. */ - } + } /** * Deletes the keyword search text for a specific data source. @@ -104,6 +104,6 @@ public interface KeywordSearchService extends Closeable { * * @throws KeywordSearchServiceException if unable to delete. */ - public void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException; + void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException; } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties index df3db1bf73..06e35314ce 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties @@ -298,6 +298,7 @@ GlobalEditListPanel.editWordButton.text=Edit Keyword SolrSearchService.ServiceName=Solr Keyword Search Service SolrSearchService.IndexReadOnlyDialog.title=Text Index Is Read-Only SolrSearchService.IndexReadOnlyDialog.msg=The text index for this case is read-only.
You will be able to see existing keyword search results and perform exact match and substring match keyword searches,
but you will not be able to add new text to the index or perform regex searches. You may instead open the case
with your previous version of this application. +SolrSearchService.DeleteDataSource.msg=Error Deleting Solr data for data source id {0} ExtractedContentPanel.jLabel1.text=Text Source: ExtractedContentPanel.pagePreviousButton.actionCommand=pagePreviousButton ExtractedContentPanel.pagePreviousButton.text= diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED index c44ceb7556..d44a070c9b 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED @@ -36,7 +36,7 @@ KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found KeywordSearchResultFactory.query.exception.msg=Could not perform the query OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\n\The module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found. +OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\nThe module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found. OpenIDE-Module-Name=KeywordSearch OptionsCategory_Name_KeywordSearchOptions=Keyword Search OptionsCategory_Keywords_KeywordSearchOptions=Keyword Search @@ -358,6 +358,7 @@ SolrSearchService.indexingError=Unable to index blackboard artifact. SolrSearchService.ServiceName=Solr Keyword Search Service SolrSearchService.IndexReadOnlyDialog.title=Text Index Is Read-Only SolrSearchService.IndexReadOnlyDialog.msg=The text index for this case is read-only.
You will be able to see existing keyword search results and perform exact match and substring match keyword searches,
but you will not be able to add new text to the index or perform regex searches. You may instead open the case
with your previous version of this application. +SolrSearchService.DeleteDataSource.msg=Error Deleting Solr data for data source id {0} ExtractedContentPanel.jLabel1.text=Text Source: ExtractedContentPanel.pagePreviousButton.actionCommand=pagePreviousButton ExtractedContentPanel.pagePreviousButton.text= diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index dae1bae943..b73d274c84 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -1277,14 +1277,17 @@ public class Server { * * @throws NoOpenCoreException */ - public void deleteDataSource(Long dataSourceId) throws SolrServerException, KeywordSearchModuleException, NoOpenCoreException, IOException { - currentCoreLock.writeLock().lock(); - if (null == currentCore) { - throw new NoOpenCoreException(); + void deleteDataSource(Long dataSourceId) throws IOException, KeywordSearchModuleException, NoOpenCoreException, SolrServerException { + try { + currentCoreLock.writeLock().lock(); + if (null == currentCore) { + throw new NoOpenCoreException(); + } + currentCore.deleteDataSource(dataSourceId); + currentCore.commit(); + } finally { + currentCoreLock.writeLock().unlock(); } - currentCore.deleteDataSource(dataSourceId); - currentCore.commit(); - currentCoreLock.writeLock().unlock(); } /** @@ -1528,11 +1531,11 @@ public class Server { } } - private void deleteDataSource(Long dsObjId) throws SolrServerException, IOException { + private void deleteDataSource(Long dsObjId) throws IOException, SolrServerException { String dataSourceId = Long.toString(dsObjId); String deleteQuery = "image_id:" + dataSourceId; - // Get the first result. - UpdateResponse updateResponse = solrCore.deleteByQuery(deleteQuery); + + solrCore.deleteByQuery(deleteQuery); } void addDocument(SolrInputDocument doc) throws KeywordSearchModuleException { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java index 13eb75907f..e2906f89d6 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java @@ -60,8 +60,7 @@ import org.sleuthkit.datamodel.TskCoreException; * text indexing and search. */ @ServiceProviders(value = { - @ServiceProvider(service = KeywordSearchService.class) - , + @ServiceProvider(service = KeywordSearchService.class), @ServiceProvider(service = AutopsyService.class) }) public class SolrSearchService implements KeywordSearchService, AutopsyService { @@ -196,24 +195,24 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService { /** * Deletes a data source from Solr for a case. - * + * * @param dataSourceId the id of the data source to delete. - * - * @throws org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException + * + * @throws + * org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException */ - @NbBundle.Messages({ - "SolrSearchService.deleteDataSource.exceptionMessage.noCurrentSolrCore=DeleteDataSource did not contain a current Solr core so could not delete the Data Source", - }) @Override public void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException { + try { - KeywordSearch.getServer().deleteDataSource(dataSourceId); - } catch (NoOpenCoreException | KeywordSearchModuleException | SolrServerException | IOException ex) { - throw new KeywordSearchServiceException(NbBundle.getMessage(SolrSearchService.class, - "SolrSearchService.deleteDataSource.exceptionMessage.noCurrentSolrCore")); + Server ddsServer = KeywordSearch.getServer(); + ddsServer.deleteDataSource(dataSourceId); + } catch (IOException | KeywordSearchModuleException | NoOpenCoreException | SolrServerException ex) { + logger.log(Level.WARNING, NbBundle.getMessage(SolrSearchService.class, "SolrSearchService.DeleteDataSource.msg", dataSourceId), ex); + throw new KeywordSearchServiceException(NbBundle.getMessage(SolrSearchService.class, "SolrSearchService.DeleteDataSource.msg", dataSourceId), ex); } } - + /** * Deletes Solr core for a case. * From c0e3a2d33e9b9ea8b6c3f7ec552421dc4533af1b Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 22 Oct 2019 14:22:17 -0400 Subject: [PATCH 40/81] Update Server.java Removed unused import. --- .../src/org/sleuthkit/autopsy/keywordsearch/Server.java | 1 - 1 file changed, 1 deletion(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index b73d274c84..a4a3b581b3 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -55,7 +55,6 @@ import org.apache.solr.client.solrj.request.CoreAdminRequest; import org.apache.solr.client.solrj.response.CoreAdminResponse; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.response.TermsResponse; -import org.apache.solr.client.solrj.response.UpdateResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrException; From aa6122ce8f29c6cdd7ce9b470db79738b43a47aa Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 28 Oct 2019 10:20:22 -0400 Subject: [PATCH 41/81] Format DeleteDataSourceAction.java --- .../sleuthkit/autopsy/actions/DeleteDataSourceAction.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index 1292e079d4..15664110a3 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -34,9 +34,10 @@ import org.sleuthkit.datamodel.TskCoreException; * Instances of this Action allow users to delete the specified data source. */ public final class DeleteDataSourceAction extends AbstractAction { + private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName()); private final Long dataSourceId; - + @NbBundle.Messages({"DeleteDataSourceAction.name.text=Delete Data Source"}) public DeleteDataSourceAction(Long dataSourceId) { super(Bundle.DeleteDataSourceAction_name_text()); @@ -52,6 +53,7 @@ public final class DeleteDataSourceAction extends AbstractAction { logger.log(Level.WARNING, "Error Deleting Data source " + dataSourceId, e); } } + private static void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException { try { KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); @@ -59,6 +61,6 @@ public final class DeleteDataSourceAction extends AbstractAction { } catch (KeywordSearchServiceException e) { logger.log(Level.WARNING, "KWS Error", e); } - + } } From 66beead5c511433f82ee8cf410b97e9b57d98d98 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 28 Oct 2019 11:43:38 -0400 Subject: [PATCH 42/81] Address IDE hints for DeleteDataSourceAction.java --- .../actions/DeleteDataSourceAction.java | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index 15664110a3..21690120c3 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -31,36 +31,47 @@ import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException; import org.sleuthkit.datamodel.TskCoreException; /** - * Instances of this Action allow users to delete the specified data source. + * Instances of this Action allow users to delete data sources. */ public final class DeleteDataSourceAction extends AbstractAction { + private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName()); - private final Long dataSourceId; + private long dataSourceID; - @NbBundle.Messages({"DeleteDataSourceAction.name.text=Delete Data Source"}) - public DeleteDataSourceAction(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; + this.dataSourceID = dataSourceID; } @Override public void actionPerformed(ActionEvent event) { try { - Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(dataSourceId); - deleteDataSource(dataSourceId); - } catch (NoCurrentCaseException | TskCoreException | KeywordSearchServiceException e) { - logger.log(Level.WARNING, "Error Deleting Data source " + dataSourceId, e); - } - } - - private static void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException { - try { + Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(dataSourceID); KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); - kwsService.deleteDataSource(dataSourceId); - } catch (KeywordSearchServiceException e) { - logger.log(Level.WARNING, "KWS Error", e); + kwsService.deleteDataSource(dataSourceID); + } catch (NoCurrentCaseException | TskCoreException | KeywordSearchServiceException e) { + logger.log(Level.WARNING, String.format("Error Deleting data source (obj_id=%d)", dataSourceID), e); } - } + + @Override + public Object clone() throws CloneNotSupportedException { + Object clonedObject = super.clone(); + ((DeleteDataSourceAction) clonedObject).setDataSourceID(this.dataSourceID); + return clonedObject; + } + + private void setDataSourceID(long dataSourceID) { + this.dataSourceID = dataSourceID; + } + } From 9b4ad347bd63edc8cddcf829d06a9b97d749e4a1 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 28 Oct 2019 13:55:01 -0400 Subject: [PATCH 43/81] Merge in new data source deletion code from McKinnon --- .../sleuthkit/autopsy/actions/DeleteDataSourceAction.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index 3ac6f0eec8..3909c59827 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -65,9 +65,9 @@ public final class DeleteDataSourceAction extends AbstractAction { } @Override - public Object clone() throws CloneNotSupportedException { - Object clonedObject = super.clone(); - ((DeleteDataSourceAction) clonedObject).setDataSourceID(this.dataSourceID); + public DeleteDataSourceAction clone() throws CloneNotSupportedException { + DeleteDataSourceAction clonedObject = ((DeleteDataSourceAction) super.clone()); + clonedObject.setDataSourceID(this.dataSourceID); return clonedObject; } From 5cb4348f34272fa3de1fa47d8d040b4e16d7495f Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 28 Oct 2019 14:10:39 -0400 Subject: [PATCH 44/81] Merge in new data source deletion code from McKinnon --- .../actions/DeleteDataSourceAction.java | 4 +-- .../sleuthkit/autopsy/casemodule/Case.java | 14 +++++----- .../events/DataSourceDeletedEvent.java | 28 ++++++++++--------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java index 3909c59827..b71f9db7a2 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/DeleteDataSourceAction.java @@ -31,7 +31,7 @@ import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException; import org.sleuthkit.datamodel.TskCoreException; /** - * Instances of this Action allow users to delete data sources. + * An Action that allows a user to delete a data source. */ public final class DeleteDataSourceAction extends AbstractAction { @@ -60,7 +60,7 @@ public final class DeleteDataSourceAction extends AbstractAction { kwsService.deleteDataSource(dataSourceID); Case.getCurrentCaseThrows().notifyDataSourceDeleted(dataSourceID); } catch (NoCurrentCaseException | TskCoreException | KeywordSearchServiceException e) { - logger.log(Level.WARNING, String.format("Error Deleting data source (obj_id=%d)", dataSourceID), e); + logger.log(Level.SEVERE, String.format("Error Deleting data source (obj_id=%d)", dataSourceID), e); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 300b563d02..e70607424e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -399,9 +399,9 @@ public class Case { * TimelineEvent that was added. */ TIMELINE_EVENT_ADDED, - /* An item in the central repository has had its comment - * modified. The old value is null, the new value is string for current - * comment. + /* + * An item in the central repository has had its comment modified. The + * old value is null, the new value is string for current comment. */ CR_COMMENT_CHANGED; @@ -536,8 +536,8 @@ public class Case { */ public static boolean isValidName(String caseName) { return !(caseName.contains("\\") || caseName.contains("/") || caseName.contains(":") - || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"") - || caseName.contains("<") || caseName.contains(">") || caseName.contains("|")); + || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"") + || caseName.contains("<") || caseName.contains(">") || caseName.contains("|")); } /** @@ -1484,12 +1484,12 @@ public class Case { } /** - * Notifies case event subscribers that a data source has been delete from + * 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 data source that was deleted. + * @param dataSourceId The object ID of the data source that was deleted. */ public void notifyDataSourceDeleted(Long dataSourceId) { eventPublisher.publish(new DataSourceDeletedEvent(dataSourceId)); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java index 5317a951d0..5550ad9313 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015-2019 Basis Technology Corp. + * Copyright 2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,36 +16,38 @@ * 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; +/** + * An application event that is published when a data source has been deleted. + */ public class DataSourceDeletedEvent extends AutopsyEvent implements Serializable { private static final long serialVersionUID = 1L; - private final long dataSourceId; - + private final long dataSourceID; + /** - * Constructs an event published when a data source is added to a case. + * Constructs an application event that is published when a data source has + * been deleted. * - * @param dataSourceId The data source that was deleted. + * @param dataSourceId The object ID of the data source that was deleted. */ public DataSourceDeletedEvent(Long dataSourceId) { - super(Case.Events.DATA_SOURCE_DELETED.toString(), null, dataSourceId); - this.dataSourceId = dataSourceId; - } - + this.dataSourceID = dataSourceId; + } + /** - * Gets the data source id that is being deleted + * Gets the object ID of the data source that was deleted. * * @return The data source id. */ public long getDataSourceId() { - return dataSourceId; + return dataSourceID; } - + } From 4c3a25346ad462272ab468a84ee8066a17f38f88 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 28 Oct 2019 16:12:57 -0400 Subject: [PATCH 45/81] Merge in new data source deletion code from McKinnon --- .../imagegallery/ImageGalleryController.java | 14 +++++++++++--- .../imagegallery/datamodel/DrawableDB.java | 17 ++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index 98967715ff..74680d8036 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -23,6 +23,7 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.sql.SQLException; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; @@ -49,6 +50,7 @@ import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import org.apache.commons.collections4.CollectionUtils; import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case.CaseType; @@ -104,7 +106,7 @@ public final class ImageGalleryController { Case.Events.DATA_SOURCE_ADDED, Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, - Case.Events.DATA_SOURCE_DELETED + Case.Events.DATA_SOURCE_DELETED ); /* @@ -807,8 +809,14 @@ public final class ImageGalleryController { case DATA_SOURCE_DELETED: if (((AutopsyEvent) event).getSourceType() == AutopsyEvent.SourceType.LOCAL) { final DataSourceDeletedEvent dataSourceDeletedEvent = (DataSourceDeletedEvent) event; - long dataSoureObjId = dataSourceDeletedEvent.getDataSourceId(); - drawableDB.deleteDataSource(dataSoureObjId); + long dataSourceObjId = dataSourceDeletedEvent.getDataSourceId(); + try { + drawableDB.deleteDataSource(dataSourceObjId); + } catch (SQLException ex) { + logger.log(Level.SEVERE, String.format("Failed to delete data source (obj_id = %d)", dataSourceObjId), ex); //NON-NLS + } catch (TskCoreException ex) { + Exceptions.printStackTrace(ex); + } } break; case CONTENT_TAG_ADDED: diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java index 1d7a8fca5b..a4332feb6e 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java @@ -106,7 +106,7 @@ public final class DrawableDB { private static final String IG_CREATION_SCHEMA_MAJOR_VERSION_KEY = "IG_CREATION_SCHEMA_MAJOR_VERSION"; private static final String IG_CREATION_SCHEMA_MINOR_VERSION_KEY = "IG_CREATION_SCHEMA_MINOR_VERSION"; - private static final VersionNumber IG_STARTING_SCHEMA_VERSION = new VersionNumber(1, 2, 0); // IG Schema Starting version + private static final VersionNumber IG_STARTING_SCHEMA_VERSION = new VersionNumber(1, 0, 0); // IG Schema Starting version - DO NOT CHANGE private static final VersionNumber IG_SCHEMA_VERSION = new VersionNumber(1, 2, 0); // IG Schema Current version private PreparedStatement insertHashSetStmt; @@ -582,10 +582,6 @@ public final class DrawableDB { + " make TEXT DEFAULT NULL, " //NON-NLS + " model TEXT DEFAULT NULL, " //NON-NLS + " analyzed integer DEFAULT 0, " //NON-NLS - + " FOREIGN KEY (data_source_obj_id) REFERENCES datasources(ds_obj_id) ON DELETE CASCADE)" //NON-NLS - + " make VARCHAR(255) DEFAULT NULL, " //NON-NLS - + " model VARCHAR(255) DEFAULT NULL, " //NON-NLS - + " analyzed integer DEFAULT 0, " //NON-NLS + " FOREIGN KEY (data_source_obj_id) REFERENCES datasources(ds_obj_id) ON DELETE CASCADE)"; //NON-NLS stmt.execute(sql); } catch (SQLException ex) { @@ -2041,7 +2037,7 @@ public final class DrawableDB { * * @param id the obj_id of the row to be deleted */ - public void deleteDataSource(long dataSourceId) { + public void deleteDataSource(long dataSourceId) throws SQLException, TskCoreException { dbWriteLock(); DrawableTransaction trans = null; try { @@ -2050,7 +2046,14 @@ public final class DrawableDB { deleteDataSourceStmt.executeUpdate(); commitTransaction(trans, true); } catch (SQLException | TskCoreException ex) { - logger.log(Level.WARNING, "failed to deletesource for obj_id = " + dataSourceId, ex); //NON-NLS + if (null != trans) { + try { + rollbackTransaction(trans); + } catch (SQLException ex2) { + logger.log(Level.SEVERE, String.format("Failed to roll back drawables db transaction after error: %s", ex.getMessage()), ex2); //NON-NLS + } + } + throw ex; } finally { dbWriteUnlock(); } From 379574942df9480673cae127b518c77d35de4852 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 28 Oct 2019 16:14:08 -0400 Subject: [PATCH 46/81] Merge in new data source deletion code from McKinnon --- .../sleuthkit/autopsy/imagegallery/ImageGalleryController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index 74680d8036..bd048e4d9a 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -50,7 +50,6 @@ import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import org.apache.commons.collections4.CollectionUtils; import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case.CaseType; From 45cff4ad542a86f8c32f49ec2f893c87c6085398 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 28 Oct 2019 16:14:59 -0400 Subject: [PATCH 47/81] Merge in new data source deletion code from McKinnon --- .../sleuthkit/autopsy/imagegallery/Bundle.properties-MERGED | 5 +---- .../autopsy/imagegallery/ImageGalleryController.java | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties-MERGED b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties-MERGED index 9b75078ae8..087afc8a83 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties-MERGED +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties-MERGED @@ -26,10 +26,7 @@ ImageGalleryTopComponent.chooseDataSourceDialog.all=All ImageGalleryTopComponent.chooseDataSourceDialog.contentText=Data source: ImageGalleryTopComponent.chooseDataSourceDialog.headerText=Choose a data source to view. ImageGalleryTopComponent.chooseDataSourceDialog.titleText=Image Gallery -OpenIDE-Module-Long-Description=\ - New image and video gallery that has been designed to make performing image-intensive investigations more efficient. \ - This work has been funded by DHS S&T and this is a beta release. \ - It is not available on the sleuthkit.org site and has been distributed to limited users. +OpenIDE-Module-Long-Description=New image and video gallery that has been designed to make performing image-intensive investigations more efficient. This work has been funded by DHS S&T and this is a beta release. It is not available on the sleuthkit.org site and has been distributed to limited users. OpenIDE-Module-Name=ImageGallery OpenIDE-Module-Short-Description=Advanced image and video gallery ImageGalleryOptionsPanel.enabledForCaseBox.text=Enable Image Gallery updates for the current case. diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index bd048e4d9a..088e0b8140 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -811,10 +811,8 @@ public final class ImageGalleryController { long dataSourceObjId = dataSourceDeletedEvent.getDataSourceId(); try { drawableDB.deleteDataSource(dataSourceObjId); - } catch (SQLException ex) { + } catch (SQLException | TskCoreException ex) { logger.log(Level.SEVERE, String.format("Failed to delete data source (obj_id = %d)", dataSourceObjId), ex); //NON-NLS - } catch (TskCoreException ex) { - Exceptions.printStackTrace(ex); } } break; From d07f80fd00f2068e73838b6f662f5a8a24dd5690 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 28 Oct 2019 16:31:24 -0400 Subject: [PATCH 48/81] Merge in new data source deletion code from McKinnon --- .../sleuthkit/autopsy/imagegallery/Bundle.properties-MERGED | 5 ++++- .../sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED | 1 - .../core.jar/org/netbeans/core/startup/Bundle.properties | 2 +- .../org/netbeans/core/windows/view/ui/Bundle.properties | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties-MERGED b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties-MERGED index 087afc8a83..9b75078ae8 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties-MERGED +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties-MERGED @@ -26,7 +26,10 @@ ImageGalleryTopComponent.chooseDataSourceDialog.all=All ImageGalleryTopComponent.chooseDataSourceDialog.contentText=Data source: ImageGalleryTopComponent.chooseDataSourceDialog.headerText=Choose a data source to view. ImageGalleryTopComponent.chooseDataSourceDialog.titleText=Image Gallery -OpenIDE-Module-Long-Description=New image and video gallery that has been designed to make performing image-intensive investigations more efficient. This work has been funded by DHS S&T and this is a beta release. It is not available on the sleuthkit.org site and has been distributed to limited users. +OpenIDE-Module-Long-Description=\ + New image and video gallery that has been designed to make performing image-intensive investigations more efficient. \ + This work has been funded by DHS S&T and this is a beta release. \ + It is not available on the sleuthkit.org site and has been distributed to limited users. OpenIDE-Module-Name=ImageGallery OpenIDE-Module-Short-Description=Advanced image and video gallery ImageGalleryOptionsPanel.enabledForCaseBox.text=Enable Image Gallery updates for the current case. diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED index d44a070c9b..d2fe0dd2db 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED @@ -348,7 +348,6 @@ SolrSearch.openCore.msg=Opening text index SolrSearch.openGiantCore.msg=Opening text index. Text index for this case is very large and may take long time to load. SolrSearch.openLargeCore.msg=Opening text index. This may take several minutes. SolrSearch.readingIndexes.msg=Reading text index metadata file -SolrSearchService.deleteDataSource.exceptionMessage.noCurrentSolrCore=DeleteDataSource did not contain a current Solr core so could not delete the Data Source # {0} - index folder path SolrSearchService.exceptionMessage.failedToDeleteIndexFiles=Failed to delete text index files at {0} SolrSearchService.exceptionMessage.noCurrentSolrCore=IndexMetadata did not contain a current Solr core so could not delete the case 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 4fd4fb33d8..6e10156aef 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, 04 Oct 2019 14:30:10 -0400 +#Mon, 28 Oct 2019 16:22:16 -0400 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 17f4cb7436..79b6f9b6fe 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, 04 Oct 2019 14:30:10 -0400 +#Mon, 28 Oct 2019 16:22:16 -0400 CTL_MainWindow_Title=Autopsy 4.13.0 CTL_MainWindow_Title_No_Project=Autopsy 4.13.0 From 0573c4cb8e3500627c6fc2348d0c794fe1f9f743 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 28 Oct 2019 16:43:41 -0400 Subject: [PATCH 49/81] Merge in new data source deletion code from McKinnon --- .../imagegallery/datamodel/DrawableDB.java | 421 +++++++++--------- 1 file changed, 212 insertions(+), 209 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java index a4332feb6e..d875657cc1 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java @@ -100,15 +100,15 @@ public final class DrawableDB { private static final String GROUPS_SEEN_TABLENAME = "image_gallery_groups_seen"; //NON-NLS private static final String IG_DB_INFO_TABLE = "image_gallery_db_info"; - + private static final String IG_SCHEMA_MAJOR_VERSION_KEY = "IG_SCHEMA_MAJOR_VERSION"; private static final String IG_SCHEMA_MINOR_VERSION_KEY = "IG_SCHEMA_MINOR_VERSION"; private static final String IG_CREATION_SCHEMA_MAJOR_VERSION_KEY = "IG_CREATION_SCHEMA_MAJOR_VERSION"; private static final String IG_CREATION_SCHEMA_MINOR_VERSION_KEY = "IG_CREATION_SCHEMA_MINOR_VERSION"; - + private static final VersionNumber IG_STARTING_SCHEMA_VERSION = new VersionNumber(1, 0, 0); // IG Schema Starting version - DO NOT CHANGE private static final VersionNumber IG_SCHEMA_VERSION = new VersionNumber(1, 2, 0); // IG Schema Current version - + private PreparedStatement insertHashSetStmt; private List preparedStatements = new ArrayList<>(); @@ -145,7 +145,7 @@ public final class DrawableDB { private PreparedStatement hashSetGroupStmt; private PreparedStatement pathGroupFilterByDataSrcStmt; - + private PreparedStatement deleteDataSourceStmt; /** @@ -407,11 +407,12 @@ public final class DrawableDB { /** * Checks if the specified table exists in Drawable DB - * + * * @param tableName table to check + * * @return true if the table exists in the database - * - * @throws SQLException + * + * @throws SQLException */ private boolean doesTableExist(String tableName) throws SQLException { ResultSet tableQueryResults = null; @@ -424,15 +425,14 @@ public final class DrawableDB { break; } } - } - finally { + } finally { if (tableQueryResults != null) { tableQueryResults.close(); - } + } } return tableExists; } - + private static void deleteDatabaseIfOlderVersion(Path dbPath) throws SQLException, IOException { if (Files.exists(dbPath)) { boolean hasDrawableFilesTable = false; @@ -518,7 +518,7 @@ public final class DrawableDB { dbWriteLock(); try { boolean drawableDbTablesExist = true; - + if (isClosed()) { logger.log(Level.SEVERE, "The drawables database is closed"); //NON-NLS return false; @@ -535,31 +535,31 @@ public final class DrawableDB { * Create tables in the drawables database. */ try (Statement stmt = con.createStatement()) { - + // Check if the database is new or an existing database drawableDbTablesExist = doesTableExist("drawable_files"); if (false == doesTableExist(IG_DB_INFO_TABLE)) { try { - VersionNumber ig_creation_schema_version = drawableDbTablesExist - ? IG_STARTING_SCHEMA_VERSION - : IG_SCHEMA_VERSION; - + VersionNumber ig_creation_schema_version = drawableDbTablesExist + ? IG_STARTING_SCHEMA_VERSION + : IG_SCHEMA_VERSION; + stmt.execute("CREATE TABLE IF NOT EXISTS " + IG_DB_INFO_TABLE + " (name TEXT PRIMARY KEY, value TEXT NOT NULL)"); - + // backfill creation schema ver - stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_CREATION_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor() )); - stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_CREATION_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor() )); - + stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_CREATION_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor())); + stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_CREATION_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor())); + // set current schema ver: at DB initialization - current version is same as starting version - stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor() )); - stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor() )); - + stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor())); + stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor())); + } catch (SQLException ex) { logger.log(Level.SEVERE, "Failed to create ig_db_info table", ex); //NON-NLS return false; } } - + try { String sql = "CREATE TABLE IF NOT EXISTS datasources " //NON-NLS + "( id INTEGER PRIMARY KEY, " //NON-NLS @@ -654,45 +654,45 @@ public final class DrawableDB { * Create tables in the case database. */ String autogenKeyType = (DbType.POSTGRESQL == tskCase.getDatabaseType()) ? "BIGSERIAL" : "INTEGER"; - + try { - boolean caseDbTablesExist = tskCase.getCaseDbAccessManager().tableExists(GROUPS_TABLENAME); - VersionNumber ig_creation_schema_version = caseDbTablesExist - ? IG_STARTING_SCHEMA_VERSION - : IG_SCHEMA_VERSION; - - String tableSchema = "( id " + autogenKeyType + " PRIMARY KEY, " - + " name TEXT UNIQUE NOT NULL," - + " value TEXT NOT NULL )"; - tskCase.getCaseDbAccessManager().createTable(IG_DB_INFO_TABLE, tableSchema); - - // backfill creation version - String creationMajorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_CREATION_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor()); - String creationMinorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_CREATION_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor()); - - // set current version - at the onset, current version is same as creation version - String currentMajorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor()); - String currentMinorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor()); - - if (DbType.POSTGRESQL == tskCase.getDatabaseType()) { - creationMajorVerSQL += " ON CONFLICT DO NOTHING "; - creationMinorVerSQL += " ON CONFLICT DO NOTHING "; - - currentMajorVerSQL += " ON CONFLICT DO NOTHING "; - currentMinorVerSQL += " ON CONFLICT DO NOTHING "; - } - - tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, creationMajorVerSQL); - tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, creationMinorVerSQL); - - tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, currentMajorVerSQL); - tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, currentMinorVerSQL); - - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Failed to create ig_db_info table in Case database", ex); //NON-NLS - return false; - } - + boolean caseDbTablesExist = tskCase.getCaseDbAccessManager().tableExists(GROUPS_TABLENAME); + VersionNumber ig_creation_schema_version = caseDbTablesExist + ? IG_STARTING_SCHEMA_VERSION + : IG_SCHEMA_VERSION; + + String tableSchema = "( id " + autogenKeyType + " PRIMARY KEY, " + + " name TEXT UNIQUE NOT NULL," + + " value TEXT NOT NULL )"; + tskCase.getCaseDbAccessManager().createTable(IG_DB_INFO_TABLE, tableSchema); + + // backfill creation version + String creationMajorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_CREATION_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor()); + String creationMinorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_CREATION_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor()); + + // set current version - at the onset, current version is same as creation version + String currentMajorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor()); + String currentMinorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor()); + + if (DbType.POSTGRESQL == tskCase.getDatabaseType()) { + creationMajorVerSQL += " ON CONFLICT DO NOTHING "; + creationMinorVerSQL += " ON CONFLICT DO NOTHING "; + + currentMajorVerSQL += " ON CONFLICT DO NOTHING "; + currentMinorVerSQL += " ON CONFLICT DO NOTHING "; + } + + tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, creationMajorVerSQL); + tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, creationMinorVerSQL); + + tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, currentMajorVerSQL); + tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, currentMinorVerSQL); + + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Failed to create ig_db_info table in Case database", ex); //NON-NLS + return false; + } + try { String tableSchema = "( group_id " + autogenKeyType + " PRIMARY KEY, " //NON-NLS @@ -734,20 +734,21 @@ public final class DrawableDB { /** * Gets the Schema version from DrawableDB - * + * * @return image gallery schema version in DrawableDB + * * @throws SQLException - * @throws TskCoreException + * @throws TskCoreException */ private VersionNumber getDrawableDbIgSchemaVersion() throws SQLException, TskCoreException { - + Statement statement = con.createStatement(); ResultSet resultSet = null; - try { + try { int majorVersion = -1; String majorVersionStr = null; - resultSet = statement.executeQuery(String.format("SELECT value FROM %s WHERE name='%s'", IG_DB_INFO_TABLE, IG_SCHEMA_MAJOR_VERSION_KEY)); + resultSet = statement.executeQuery(String.format("SELECT value FROM %s WHERE name='%s'", IG_DB_INFO_TABLE, IG_SCHEMA_MAJOR_VERSION_KEY)); if (resultSet.next()) { majorVersionStr = resultSet.getString("value"); try { @@ -756,12 +757,12 @@ public final class DrawableDB { throw new TskCoreException("Bad value for schema major version = " + majorVersionStr, ex); } } else { - throw new TskCoreException("Failed to read schema major version from ig_db_info table"); + throw new TskCoreException("Failed to read schema major version from ig_db_info table"); } - + int minorVersion = -1; String minorVersionStr = null; - resultSet = statement.executeQuery(String.format("SELECT value FROM %s WHERE name='%s'", IG_DB_INFO_TABLE, IG_SCHEMA_MINOR_VERSION_KEY)); + resultSet = statement.executeQuery(String.format("SELECT value FROM %s WHERE name='%s'", IG_DB_INFO_TABLE, IG_SCHEMA_MINOR_VERSION_KEY)); if (resultSet.next()) { minorVersionStr = resultSet.getString("value"); try { @@ -773,9 +774,8 @@ public final class DrawableDB { throw new TskCoreException("Failed to read schema minor version from ig_db_info table"); } - return new VersionNumber(majorVersion, minorVersion, 0 ); - } - finally { + return new VersionNumber(majorVersion, minorVersion, 0); + } finally { if (resultSet != null) { resultSet.close(); } @@ -787,27 +787,28 @@ public final class DrawableDB { /** * Gets the ImageGallery schema version from CaseDB - * + * * @return image gallery schema version in CaseDB + * * @throws SQLException - * @throws TskCoreException + * @throws TskCoreException */ private VersionNumber getCaseDbIgSchemaVersion() throws TskCoreException { - + // Callback to process result of get version query class GetSchemaVersionQueryResultProcessor implements CaseDbAccessQueryCallback { private int version = -1; - - int getVersion() { + + int getVersion() { return version; } - + @Override public void process(ResultSet resultSet) { - try { - if (resultSet.next()) { - String versionStr = resultSet.getString("value"); + try { + if (resultSet.next()) { + String versionStr = resultSet.getString("value"); try { version = Integer.parseInt(versionStr); } catch (NumberFormatException ex) { @@ -816,100 +817,96 @@ public final class DrawableDB { } else { logger.log(Level.SEVERE, "Failed to get version"); } - } - catch (SQLException ex) { + } catch (SQLException ex) { logger.log(Level.SEVERE, "Failed to get version", ex); //NON-NLS } } } - + GetSchemaVersionQueryResultProcessor majorVersionResultProcessor = new GetSchemaVersionQueryResultProcessor(); GetSchemaVersionQueryResultProcessor minorVersionResultProcessor = new GetSchemaVersionQueryResultProcessor(); - - String versionQueryTemplate = "value FROM %s WHERE name = \'%s\' "; + + String versionQueryTemplate = "value FROM %s WHERE name = \'%s\' "; tskCase.getCaseDbAccessManager().select(String.format(versionQueryTemplate, IG_DB_INFO_TABLE, IG_SCHEMA_MAJOR_VERSION_KEY), majorVersionResultProcessor); tskCase.getCaseDbAccessManager().select(String.format(versionQueryTemplate, IG_DB_INFO_TABLE, IG_SCHEMA_MINOR_VERSION_KEY), minorVersionResultProcessor); - + return new VersionNumber(majorVersionResultProcessor.getVersion(), minorVersionResultProcessor.getVersion(), 0); } - + /** * Updates the IG schema version in the Drawable DB - * - * @param version new version number + * + * @param version new version number * @param transaction transaction under which the update happens - * + * * @throws SQLException */ private void updateDrawableDbIgSchemaVersion(VersionNumber version, DrawableTransaction transaction) throws SQLException, TskCoreException { - + if (transaction == null) { - throw new TskCoreException("Schema version update must be done in a transaction"); + throw new TskCoreException("Schema version update must be done in a transaction"); } - + dbWriteLock(); try { Statement statement = con.createStatement(); - + // update schema version - statement.execute(String.format("UPDATE %s SET value = '%s' WHERE name = '%s'", IG_DB_INFO_TABLE, version.getMajor(), IG_SCHEMA_MAJOR_VERSION_KEY )); - statement.execute(String.format("UPDATE %s SET value = '%s' WHERE name = '%s'", IG_DB_INFO_TABLE, version.getMinor(), IG_SCHEMA_MINOR_VERSION_KEY )); - + statement.execute(String.format("UPDATE %s SET value = '%s' WHERE name = '%s'", IG_DB_INFO_TABLE, version.getMajor(), IG_SCHEMA_MAJOR_VERSION_KEY)); + statement.execute(String.format("UPDATE %s SET value = '%s' WHERE name = '%s'", IG_DB_INFO_TABLE, version.getMinor(), IG_SCHEMA_MINOR_VERSION_KEY)); + statement.close(); - } - finally { + } finally { dbWriteUnlock(); } } - + /** * Updates the IG schema version in CaseDB - * - * @param version new version number + * + * @param version new version number * @param caseDbTransaction transaction to use to update the CaseDB - * + * * @throws SQLException */ private void updateCaseDbIgSchemaVersion(VersionNumber version, CaseDbTransaction caseDbTransaction) throws TskCoreException { - + String updateSQLTemplate = " SET value = %s WHERE name = '%s' "; tskCase.getCaseDbAccessManager().update(IG_DB_INFO_TABLE, String.format(updateSQLTemplate, version.getMajor(), IG_SCHEMA_MAJOR_VERSION_KEY), caseDbTransaction); - tskCase.getCaseDbAccessManager().update(IG_DB_INFO_TABLE, String.format(updateSQLTemplate, version.getMinor(), IG_SCHEMA_MINOR_VERSION_KEY), caseDbTransaction); + tskCase.getCaseDbAccessManager().update(IG_DB_INFO_TABLE, String.format(updateSQLTemplate, version.getMinor(), IG_SCHEMA_MINOR_VERSION_KEY), caseDbTransaction); } - - + /** * Upgrades the DB schema. * * @return true if the upgrade is successful - * + * * @throws SQLException - * + * */ private boolean upgradeDBSchema() throws TskCoreException, SQLException { - + // Read current version from the DBs - VersionNumber drawableDbIgSchemaVersion = getDrawableDbIgSchemaVersion(); + VersionNumber drawableDbIgSchemaVersion = getDrawableDbIgSchemaVersion(); VersionNumber caseDbIgSchemaVersion = getCaseDbIgSchemaVersion(); // Upgrade Schema in both DrawableDB and CaseDB CaseDbTransaction caseDbTransaction = tskCase.beginTransaction(); DrawableTransaction transaction = beginTransaction(); - + try { caseDbIgSchemaVersion = upgradeCaseDbIgSchema1dot0TO1dot1(caseDbIgSchemaVersion, caseDbTransaction); drawableDbIgSchemaVersion = upgradeDrawableDbIgSchema1dot0TO1dot1(drawableDbIgSchemaVersion, transaction); // update the versions in the tables - updateCaseDbIgSchemaVersion(caseDbIgSchemaVersion, caseDbTransaction ); - updateDrawableDbIgSchemaVersion(drawableDbIgSchemaVersion, transaction); - + updateCaseDbIgSchemaVersion(caseDbIgSchemaVersion, caseDbTransaction); + updateDrawableDbIgSchemaVersion(drawableDbIgSchemaVersion, transaction); + caseDbTransaction.commit(); caseDbTransaction = null; commitTransaction(transaction, false); transaction = null; - } - catch (TskCoreException | SQLException ex) { + } catch (TskCoreException | SQLException ex) { if (null != caseDbTransaction) { try { caseDbTransaction.rollback(); @@ -926,57 +923,59 @@ public final class DrawableDB { } throw ex; } - return true; + return true; } - + /** - * Upgrades IG tables in CaseDB from 1.0 to 1.1 - * Does nothing if the incoming version is not 1.0 - * - * @param currVersion version to upgrade from + * Upgrades IG tables in CaseDB from 1.0 to 1.1 Does nothing if the incoming + * version is not 1.0 + * + * @param currVersion version to upgrade from * @param caseDbTransaction transaction to use for all updates - * + * * @return new version number - * @throws TskCoreException + * + * @throws TskCoreException */ - private VersionNumber upgradeCaseDbIgSchema1dot0TO1dot1(VersionNumber currVersion, CaseDbTransaction caseDbTransaction ) throws TskCoreException { - + private VersionNumber upgradeCaseDbIgSchema1dot0TO1dot1(VersionNumber currVersion, CaseDbTransaction caseDbTransaction) throws TskCoreException { + // Upgrade if current version is 1.0 // or 1.1 - a bug in versioning alllowed some databases to be versioned as 1.1 without the actual corresponding upgrade. This allows such databases to be fixed, if needed. - if (!(currVersion.getMajor() == 1 && - (currVersion.getMinor() == 0 || currVersion.getMinor() == 1))) { + if (!(currVersion.getMajor() == 1 + && (currVersion.getMinor() == 0 || currVersion.getMinor() == 1))) { return currVersion; } - + // Add a 'is_analyzed' column to groups table in CaseDB String alterSQL = " ADD COLUMN is_analyzed integer DEFAULT 1 "; //NON-NLS - if (false == tskCase.getCaseDbAccessManager().columnExists(GROUPS_TABLENAME, "is_analyzed", caseDbTransaction )) { + if (false == tskCase.getCaseDbAccessManager().columnExists(GROUPS_TABLENAME, "is_analyzed", caseDbTransaction)) { tskCase.getCaseDbAccessManager().alterTable(GROUPS_TABLENAME, alterSQL, caseDbTransaction); - } - return new VersionNumber(1,1,0); + } + return new VersionNumber(1, 1, 0); } - + /** - * Upgrades IG tables in DrawableDB from 1.0 to 1.1 - * Does nothing if the incoming version is not 1.0 - * - * @param currVersion version to upgrade from + * Upgrades IG tables in DrawableDB from 1.0 to 1.1 Does nothing if the + * incoming version is not 1.0 + * + * @param currVersion version to upgrade from * @param transaction transaction to use for all updates - * + * * @return new version number - * @throws TskCoreException + * + * @throws TskCoreException */ - private VersionNumber upgradeDrawableDbIgSchema1dot0TO1dot1(VersionNumber currVersion, DrawableTransaction transaction ) throws TskCoreException { - - if (currVersion.getMajor() != 1 || - currVersion.getMinor() != 0) { + private VersionNumber upgradeDrawableDbIgSchema1dot0TO1dot1(VersionNumber currVersion, DrawableTransaction transaction) throws TskCoreException { + + if (currVersion.getMajor() != 1 + || currVersion.getMinor() != 0) { return currVersion; } - + // There are no changes in DrawableDB schema in 1.0 -> 1.1 - return new VersionNumber(1,1,0); + return new VersionNumber(1, 1, 0); } - + @Override protected void finalize() throws Throwable { /* @@ -1148,10 +1147,10 @@ public final class DrawableDB { } /** - * Record in the DB that the group with the given key is seen - * by given examiner id. + * Record in the DB that the group with the given key is seen by given + * examiner id. * - * @param groupKey key identifying the group. + * @param groupKey key identifying the group. * @param examinerID examiner id. * * @throws TskCoreException @@ -1159,16 +1158,16 @@ public final class DrawableDB { public void markGroupSeen(GroupKey groupKey, long examinerID) throws TskCoreException { /* - * Check the groupSeenCache to see if the seen status for this group was set recently. - * If recently set to seen, there's no need to update it + * Check the groupSeenCache to see if the seen status for this group was + * set recently. If recently set to seen, there's no need to update it */ Boolean cachedValue = groupSeenCache.getIfPresent(groupKey); if (cachedValue != null && cachedValue == true) { return; } - + // query to find the group id from attribute/value - String innerQuery = String.format("( SELECT group_id FROM " + GROUPS_TABLENAME //NON-NLS + String innerQuery = String.format("( SELECT group_id FROM " + GROUPS_TABLENAME //NON-NLS + " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d )", //NON-NLS SleuthkitCase.escapeSingleQuotes(groupKey.getAttribute().attrName.toString()), SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()), @@ -1185,8 +1184,8 @@ public final class DrawableDB { } /** - * Record in the DB that given group is unseen. - * The group is marked unseen for ALL examiners that have seen the group. + * Record in the DB that given group is unseen. The group is marked unseen + * for ALL examiners that have seen the group. * * @param groupKey key identifying the group. * @@ -1195,20 +1194,20 @@ public final class DrawableDB { public void markGroupUnseen(GroupKey groupKey) throws TskCoreException { /* - * Check the groupSeenCache to see if the seen status for this group was set recently. - * If recently set to unseen, there's no need to update it + * Check the groupSeenCache to see if the seen status for this group was + * set recently. If recently set to unseen, there's no need to update it */ Boolean cachedValue = groupSeenCache.getIfPresent(groupKey); if (cachedValue != null && cachedValue == false) { return; } - - String updateSQL = String.format(" SET seen = 0 WHERE group_id in ( " + getGroupIdQuery(groupKey) + ")" ); //NON-NLS + + String updateSQL = String.format(" SET seen = 0 WHERE group_id in ( " + getGroupIdQuery(groupKey) + ")"); //NON-NLS tskCase.getCaseDbAccessManager().update(GROUPS_SEEN_TABLENAME, updateSQL); - + groupSeenCache.put(groupKey, false); } - + /** * Sets the isAnalysed flag in the groups table for the given group to true. * @@ -1218,7 +1217,6 @@ public final class DrawableDB { */ public void markGroupAnalyzed(GroupKey groupKey) throws TskCoreException { - String updateSQL = String.format(" SET is_analyzed = %d " + " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d ", 1, @@ -1228,7 +1226,7 @@ public final class DrawableDB { tskCase.getCaseDbAccessManager().update(GROUPS_TABLENAME, updateSQL); } - + /** * Removes a file from the drawables databse. * @@ -1259,11 +1257,11 @@ public final class DrawableDB { /** * Updates the image file. - * + * * @param f file to update. - * + * * @throws TskCoreException - * @throws SQLException + * @throws SQLException */ public void updateFile(DrawableFile f) throws TskCoreException, SQLException { DrawableTransaction trans = null; @@ -1293,14 +1291,13 @@ public final class DrawableDB { } } - /** * Update an existing entry (or make a new one) into the DB that includes * group information. Called when a file has been analyzed or during a bulk * rebuild * - * @param f file to update - * @param tr + * @param f file to update + * @param tr * @param caseDbTransaction */ public void updateFile(DrawableFile f, DrawableTransaction tr, CaseDbTransaction caseDbTransaction) { @@ -1606,24 +1603,24 @@ public final class DrawableDB { } return map; } - + /** - * Get the build status for the given data source. - * Will return UNKNOWN if the data source is not yet in the database. - * + * Get the build status for the given data source. Will return UNKNOWN if + * the data source is not yet in the database. + * * @param dataSourceId - * + * * @return The status of the data source or UKNOWN if it is not found. - * - * @throws TskCoreException + * + * @throws TskCoreException */ - public DrawableDbBuildStatusEnum getDataSourceDbBuildStatus(Long dataSourceId) throws TskCoreException { + public DrawableDbBuildStatusEnum getDataSourceDbBuildStatus(Long dataSourceId) throws TskCoreException { Map statusMap = getDataSourceDbBuildStatus(); if (statusMap.containsKey(dataSourceId) == false) { return DrawableDbBuildStatusEnum.UNKNOWN; } return statusMap.get(dataSourceId); - } + } /** * Insert/update given data source object id and it's DB rebuild status in @@ -1692,47 +1689,50 @@ public final class DrawableDB { } /** - * Returns whether or not the given group is analyzed and ready to be viewed. - * + * Returns whether or not the given group is analyzed and ready to be + * viewed. + * * @param groupKey group key. + * * @return true if the group is analyzed. + * * @throws SQLException - * @throws TskCoreException + * @throws TskCoreException */ public Boolean isGroupAnalyzed(GroupKey groupKey) throws SQLException, TskCoreException { - + // Callback to process result of isAnalyzed query class IsGroupAnalyzedQueryResultProcessor implements CaseDbAccessQueryCallback { private boolean isAnalyzed = false; - - boolean getIsAnalyzed() { + + boolean getIsAnalyzed() { return isAnalyzed; } - + @Override public void process(ResultSet resultSet) { - try { - if (resultSet.next()) { - isAnalyzed = resultSet.getInt("is_analyzed") == 1 ? true: false; + try { + if (resultSet.next()) { + isAnalyzed = resultSet.getInt("is_analyzed") == 1 ? true : false; } } catch (SQLException ex) { logger.log(Level.SEVERE, "Failed to get group is_analyzed", ex); //NON-NLS } } } - + IsGroupAnalyzedQueryResultProcessor queryResultProcessor = new IsGroupAnalyzedQueryResultProcessor(); try { String groupAnalyzedQueryStmt = String.format("is_analyzed FROM " + GROUPS_TABLENAME - + " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d ", - SleuthkitCase.escapeSingleQuotes(groupKey.getAttribute().attrName.toString()), - SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()), - groupKey.getAttribute() == DrawableAttribute.PATH ? groupKey.getDataSourceObjId() : 0); - + + " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d ", + SleuthkitCase.escapeSingleQuotes(groupKey.getAttribute().attrName.toString()), + SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()), + groupKey.getAttribute() == DrawableAttribute.PATH ? groupKey.getDataSourceObjId() : 0); + tskCase.getCaseDbAccessManager().select(groupAnalyzedQueryStmt, queryResultProcessor); return queryResultProcessor.getIsAnalyzed(); - } catch ( TskCoreException ex) { + } catch (TskCoreException ex) { String msg = String.format("Failed to get group is_analyzed for group key %s", groupKey.getValueDisplayName()); //NON-NLS logger.log(Level.SEVERE, msg, ex); } @@ -1838,7 +1838,7 @@ public final class DrawableDB { // skip any null/blank values query.append("WHERE LENGTH(" + groupBy.attrName.toString() + ") > 0 "); - + if (dataSource != null) { query.append(" AND data_source_obj_id = ").append(dataSource.getId()); } @@ -1927,7 +1927,7 @@ public final class DrawableDB { return; } - int isAnalyzed = (groupBy == DrawableAttribute.PATH) ? 0 : 1; + int isAnalyzed = (groupBy == DrawableAttribute.PATH) ? 0 : 1; String insertSQL = String.format(" (data_source_obj_id, value, attribute, is_analyzed) VALUES (%d, \'%s\', \'%s\', %d)", ds_obj_id, SleuthkitCase.escapeSingleQuotes(value), SleuthkitCase.escapeSingleQuotes(groupBy.attrName.toString()), isAnalyzed); if (DbType.POSTGRESQL == tskCase.getDatabaseType()) { @@ -2000,7 +2000,6 @@ public final class DrawableDB { return countFilesWhere(" 1 "); } - /** * delete the row with obj_id = id. * @@ -2033,16 +2032,20 @@ public final class DrawableDB { } /** - * delete the datasource from the database with cascade. + * Deletes a cascading delete of a data source, starting from the + * datasources table. * - * @param id the obj_id of the row to be deleted + * @param dataSourceID The object ID of the data source to delete. + * + * @throws SQLException + * @throws TskCoreException */ - public void deleteDataSource(long dataSourceId) throws SQLException, TskCoreException { + public void deleteDataSource(long dataSourceID) throws SQLException, TskCoreException { dbWriteLock(); DrawableTransaction trans = null; try { trans = beginTransaction(); - deleteDataSourceStmt.setLong(1, dataSourceId); + deleteDataSourceStmt.setLong(1, dataSourceID); deleteDataSourceStmt.executeUpdate(); commitTransaction(trans, true); } catch (SQLException | TskCoreException ex) { @@ -2235,11 +2238,11 @@ public final class DrawableDB { // The files are processed ORDERED BY parent path // We want to preserve that order here, so that we can detect a - // change in path, and thus mark the path group as analyzed - // Hence we use a LinkedHashSet here. + // change in path, and thus mark the path group as analyzed + // Hence we use a LinkedHashSet here. private final Set updatedFiles = new LinkedHashSet<>(); private final Set removedFiles = new LinkedHashSet<>(); - + private boolean completed; private DrawableTransaction() throws TskCoreException, SQLException { From 5b4930ef5dc665ea09da7c5b706ea618ffc2cfa4 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 28 Oct 2019 16:45:30 -0400 Subject: [PATCH 50/81] Merge in new data source deletion code from McKinnon --- .../autopsy/casemodule/events/DataSourceDeletedEvent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java index 5550ad9313..3a0372fa94 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceDeletedEvent.java @@ -37,7 +37,7 @@ public class DataSourceDeletedEvent extends AutopsyEvent implements Serializable * @param dataSourceId The object ID of the data source that was deleted. */ public DataSourceDeletedEvent(Long dataSourceId) { - super(Case.Events.DATA_SOURCE_DELETED.toString(), null, dataSourceId); + super(Case.Events.DATA_SOURCE_DELETED.toString(), dataSourceId, null); this.dataSourceID = dataSourceId; } From 8db2acfe160dfe97a2ef0bf30fbdd7779ef47bd7 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 30 Oct 2019 16:42:20 -0400 Subject: [PATCH 51/81] 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 52/81] 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 53/81] 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 54/81] 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 55/81] 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 56/81] 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 57/81] 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 58/81] 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 59/81] 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 60/81] 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 61/81] 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 62/81] 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 63/81] 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 64/81] 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 65/81] 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 66/81] 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 67/81] 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 68/81] 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 69/81] 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 70/81] 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 71/81] 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 72/81] 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 73/81] 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 74/81] 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 75/81] 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 76/81] 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 77/81] 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 78/81] 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 79/81] 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 80/81] 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! From c33dbf13feb0cff6b73dc8e5cb1014e937df67e5 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 13 Nov 2019 11:25:44 -0500 Subject: [PATCH 81/81] Integrate and update data src deletion --- 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 6e50328998..99ec09d315 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -128,7 +128,7 @@ 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.SleuthkitCaseAdminUtil; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskDataException; import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException; @@ -2024,7 +2024,7 @@ public class Case { if (dataSource == null) { throw new CaseActionException(Bundle.Case_exceptionMessage_dataSourceNotFound()); } - SleuthkitCaseAdmin.deleteDataSource(this.caseDb, dataSourceObjectID); + SleuthkitCaseAdminUtil.deleteDataSource(this.caseDb, dataSourceObjectID); } catch (TskDataException | TskCoreException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSourceFromCaseDb(), ex); }