diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 6f2e6f0816d8cd72ff49ff5ffc835ad4df84e088..417239c99db58a48c3b1ed7357115cddebdb78bc 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -98,6 +98,11 @@ import org.sleuthkit.autopsy.casemodule.events.PersonsUpdatedEvent; import org.sleuthkit.autopsy.casemodule.events.PersonsDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent; +import org.sleuthkit.autopsy.casemodule.events.TagNamesEvent.TagNamesAddedEvent; +import org.sleuthkit.autopsy.casemodule.events.TagNamesEvent.TagNamesDeletedEvent; +import org.sleuthkit.autopsy.casemodule.events.TagNamesEvent.TagNamesUpdatedEvent; +import org.sleuthkit.autopsy.casemodule.events.TagSetsEvent.TagSetsAddedEvent; +import org.sleuthkit.autopsy.casemodule.events.TagSetsEvent.TagSetsDeletedEvent; import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException; import org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils; import org.sleuthkit.autopsy.casemodule.services.Services; @@ -484,7 +489,32 @@ public enum Events { /** * One or more hosts have been removed from a person. */ - HOSTS_REMOVED_FROM_PERSON; + HOSTS_REMOVED_FROM_PERSON, + + /** + * One or more TagNames have been added. + */ + TAG_NAMES_ADDED, + + /** + * One or more TagNames have been updated. + */ + TAG_NAMES_UPDATED, + + /** + * One or more TagNames have been deleted. + */ + TAG_NAMES_DELETED, + + /** + * One or more TagSets have been added. + */ + TAG_SETS_ADDED, + + /** + * One or more TagSets have been removed. + */ + TAG_SETS_DELETED; }; @@ -627,7 +657,31 @@ public void publishHostsAddedToPersonEvent(TskEvent.HostsAddedToPersonTskEvent e public void publisHostsRemovedFromPersonEvent(TskEvent.HostsRemovedFromPersonTskEvent event) { eventPublisher.publish(new HostsRemovedFromPersonEvent(event.getPerson(), event.getHostIds())); } + + @Subscribe + public void publicTagNamesAdded(TskEvent.TagNamesAddedTskEvent event) { + eventPublisher.publish(new TagNamesAddedEvent(event.getTagNames())); + } + + @Subscribe + public void publicTagNamesUpdated(TskEvent.TagNamesUpdatedTskEvent event) { + eventPublisher.publish(new TagNamesUpdatedEvent(event.getTagNames())); + } + + @Subscribe + public void publicTagNamesDeleted(TskEvent.TagNamesDeletedTskEvent event) { + eventPublisher.publish(new TagNamesDeletedEvent(event.getTagNameIds())); + } + @Subscribe + public void publicTagSetsAdded(TskEvent.TagSetsAddedTskEvent event) { + eventPublisher.publish(new TagSetsAddedEvent(event.getTagSets())); + } + + @Subscribe + public void publicTagSetsDeleted(TskEvent.TagSetsDeletedTskEvent event) { + eventPublisher.publish(new TagSetsDeletedEvent(event.getTagSetIds())); + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/TagNamesEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/TagNamesEvent.java new file mode 100755 index 0000000000000000000000000000000000000000..60ac94ff234aeb29f04c4e2b5ff7c50d8cff6a2f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/TagNamesEvent.java @@ -0,0 +1,126 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.casemodule.events; + +import java.util.ArrayList; +import java.util.List; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TagName; +import org.sleuthkit.datamodel.TaggingManager; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * A base class for TagName added and update events. + */ +public class TagNamesEvent extends TskDataModelChangedEvent<TagName, TagName> { + + private static final long serialVersionUID = 1L; + + /** + * Construct the base event for TagNames that have been added or updated. + * + * @param eventName The name of the event. + * @param tagNames The TagNames that have been modified. + */ + private TagNamesEvent(String eventName, List<TagName> tagNames) { + super(eventName, null, null, tagNames, TagName::getId); + } + + /** + * Returns a list of the added or modified TagNames. + * + * @return The event list of TagNames. + */ + public List<TagName> getTagNames() { + return getNewValue(); + } + + @Override + protected List<TagName> getNewValueObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException { + List<TagName> tagNames = new ArrayList<>(); + TaggingManager taggingMrg = caseDb.getTaggingManager(); + for (Long id : ids) { + tagNames.add(taggingMrg.getTagName(id)); + } + + return tagNames; + } + + /** + * Application events published when TagNames have been Added from the + * Sleuth Kit data model for a case. + */ + public static class TagNamesAddedEvent extends TagNamesEvent { + + private static final long serialVersionUID = 1L; + + /** + * Construct an application event published when TagNames have been + * added to the Sleuth Kit data model. + * + * @param tagNames The TagNames that have been added. + */ + public TagNamesAddedEvent(List<TagName> tagNames) { + super(Case.Events.TAG_NAMES_ADDED.name(), tagNames); + } + } + + /** + * Application events published when TagNames have been updated from the + * Sleuth Kit data model for a case. + */ + public static class TagNamesUpdatedEvent extends TagNamesEvent { + + private static final long serialVersionUID = 1L; + + /** + * Construct an application event published when TagNames have been + * updated in the Sleuth Kit data model. + * + * @param tagNames The TagNames that have been updated. + */ + public TagNamesUpdatedEvent(List<TagName> tagNames) { + super(Case.Events.TAG_NAMES_UPDATED.name(), tagNames); + } + } + + /** + * Application events published when TagNames have been deleted from the + * Sleuth Kit data model for a case. + */ + public static class TagNamesDeletedEvent extends TskDataModelObjectsDeletedEvent { + + private static final long serialVersionUID = 1L; + + /** + * Constructs an application event published when the TagNames have been + * deleted from the Sleuth Kit data model for a case. + * + * @param tagNameIds The IDs of the TagNames that have been deleted. + */ + public TagNamesDeletedEvent(List<Long> tagNameIds) { + super(Case.Events.TAG_NAMES_DELETED.name(), tagNameIds); + } + + public List<Long> getTagNameIds() { + return getOldValue(); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/TagSetsEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/TagSetsEvent.java new file mode 100755 index 0000000000000000000000000000000000000000..0896241e7d335c37b5596a65027fddffa906446c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/TagSetsEvent.java @@ -0,0 +1,103 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.casemodule.events; + +import java.util.ArrayList; +import java.util.List; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TagSet; +import org.sleuthkit.datamodel.TaggingManager; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * A base class for TagSet added and update events. + */ +public class TagSetsEvent extends TskDataModelChangedEvent<TagSet, TagSet> { + + private static final long serialVersionUID = 1L; + + /** + * Construct a new TagSetEvent. + * + * @param eventName + * @param tagSets + */ + private TagSetsEvent(String eventName, List<TagSet> tagSets) { + super(eventName, null, null, tagSets, TagSet::getId); + } + + /** + * Returns a list of the TagSet objects that were added or modified for this + * event. + * + * @return A list of TagSet objects. + */ + public List<TagSet> getTagSets() { + return this.getNewValue(); + } + + @Override + protected List<TagSet> getNewValueObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException { + List<TagSet> tagSets = new ArrayList<>(); + TaggingManager taggingMrg = caseDb.getTaggingManager(); + for (Long id : ids) { + tagSets.add(taggingMrg.getTagSet(id)); + } + return tagSets; + } + + /** + * Application events published when TagSets have been Added from the Sleuth + * Kit data model for a case. + */ + public static class TagSetsAddedEvent extends TagSetsEvent { + + private static final long serialVersionUID = 1L; + + /** + * Construct an application event published when TagSetss have been + * added to the Sleuth Kit data model. + * + * @param tagSets The TagSets that have been added. + */ + public TagSetsAddedEvent(List<TagSet> tagSets) { + super(Case.Events.TAG_SETS_ADDED.name(), tagSets); + } + } + + /** + * Application events published when TagSets have been deleted from the + * Sleuth Kit data model for a case. + */ + public static class TagSetsDeletedEvent extends TskDataModelObjectsDeletedEvent { + + private static final long serialVersionUID = 1L; + + /** + * Constructs an application event published when the TagSets have been + * deleted from the Sleuth Kit data model for a case. + * + * @param tagNameIds The IDs of the TagNames that have been deleted. + */ + public TagSetsDeletedEvent(List<Long> tagNameIds) { + super(Case.Events.TAG_SETS_DELETED.name(), tagNameIds); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java index ff07662e64971d48c42133372c763a3b31cf4873..f3645fd7c6443c06183277b2b5693a335f8e4c1d 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java @@ -236,7 +236,7 @@ private String toSettingsFormat() { TagName saveToCase(SleuthkitCase caseDb) { TagName tagName = null; try { - tagName = caseDb.addOrUpdateTagName(displayName, description, color, knownStatus); + tagName = caseDb.getTaggingManager().addOrUpdateTagName(displayName, description, color, knownStatus); } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error saving tag name definition", ex); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java index 4bb813a779af2ef9f1e3e7aa9c94d1f9591294e7..a4bd51bcce6b11d8e6e031d5e406acde2ed842cc 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java @@ -18,9 +18,12 @@ */ package org.sleuthkit.autopsy.casemodule.services; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; @@ -29,8 +32,11 @@ import java.util.Set; import java.util.logging.Level; import org.openide.util.NbBundle; +import org.openide.util.WeakListeners; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.events.TagNamesEvent; +import org.sleuthkit.autopsy.casemodule.events.TagNamesEvent.TagNamesDeletedEvent; import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -60,6 +66,37 @@ public class TagsManager implements Closeable { private static String PROJECT_VIC_TAG_SET_NAME = "Project VIC"; private static final Object lock = new Object(); + + private final Map<String, TagName> allTagNameMap = Collections.synchronizedMap(new HashMap<>()); + + private final PropertyChangeListener listener = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals(Case.Events.TAG_NAMES_ADDED.name()) + || evt.getPropertyName().equals(Case.Events.TAG_NAMES_UPDATED.name())) { + TagNamesEvent tagEvent = (TagNamesEvent) evt; + List<TagName> addTagNames = tagEvent.getTagNames(); + for (TagName tag : addTagNames) { + allTagNameMap.put(tag.getDisplayName(), tag); + } + } else if (evt.getPropertyName().equals(Case.Events.TAG_NAMES_DELETED.name())) { + TagNamesDeletedEvent tagEvent = (TagNamesDeletedEvent) evt; + List<Long> deletedIds = tagEvent.getTagNameIds(); + List<String> keysToRemove = new ArrayList<>(); + for (TagName tagName : getAllTagNames()) { + if (deletedIds.contains(tagName.getId())) { + keysToRemove.add(tagName.getDisplayName()); + } + } + + for (String key : keysToRemove) { + allTagNameMap.remove(key); + } + } + } + }; + + private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null); static { @@ -177,8 +214,6 @@ public static List<String> getNotableTagDisplayNames() { /* * No current case, nothing more to add to the set. */ - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Failed to get list of TagNames from TagsManager.", ex); } return tagDisplayNames; } @@ -268,21 +303,26 @@ public static void addTagSetDefinition(TagSetDefinition tagSetDef) throws IOExce // add the standard tag names for (TagNameDefinition def : TagNameDefinition.getStandardTagNameDefinitions()) { - caseDb.addOrUpdateTagName(def.getDisplayName(), def.getDescription(), def.getColor(), def.getKnownStatus()); + taggingMgr.addOrUpdateTagName(def.getDisplayName(), def.getDescription(), def.getColor(), def.getKnownStatus()); } //Assume new case and add all tag sets for (TagSetDefinition setDef : TagSetDefinition.readTagSetDefinitions()) { List<TagName> tagNamesInSet = new ArrayList<>(); for (TagNameDefinition tagNameDef : setDef.getTagNameDefinitions()) { - tagNamesInSet.add(caseDb.addOrUpdateTagName(tagNameDef.getDisplayName(), tagNameDef.getDescription(), tagNameDef.getColor(), tagNameDef.getKnownStatus())); + tagNamesInSet.add(taggingMgr.addOrUpdateTagName(tagNameDef.getDisplayName(), tagNameDef.getDescription(), tagNameDef.getColor(), tagNameDef.getKnownStatus())); } if (!tagNamesInSet.isEmpty()) { taggingMgr.addTagSet(setDef.getName(), tagNamesInSet); } } + } + + for(TagName tagName: caseDb.getAllTagNames()) { + allTagNameMap.put(tagName.getDisplayName(), tagName); } + } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error updating standard tag name and tag set definitions", ex); } catch (IOException ex) { @@ -292,6 +332,10 @@ public static void addTagSetDefinition(TagSetDefinition tagSetDef) throws IOExce for (TagNameDefinition tagName : TagNameDefinition.getTagNameDefinitions()) { tagName.saveToCase(caseDb); } + + Case.addEventTypeSubscriber(Collections.singleton(Case.Events.TAG_NAMES_UPDATED), weakListener); + Case.addEventTypeSubscriber(Collections.singleton(Case.Events.TAG_NAMES_ADDED), weakListener); + Case.addEventTypeSubscriber(Collections.singleton(Case.Events.TAG_NAMES_DELETED), weakListener); } /** @@ -337,11 +381,12 @@ public TagSet addTagSet(String name, List<TagName> tagNameList) throws TskCoreEx * Gets a list of all tag names currently in the case database. * * @return A list, possibly empty, of TagName objects. - * - * @throws TskCoreException If there is an error querying the case database. */ - public List<TagName> getAllTagNames() throws TskCoreException { - return caseDb.getAllTagNames(); + public synchronized List<TagName> getAllTagNames() { + + List<TagName> tagNames = new ArrayList<>(); + tagNames.addAll(allTagNameMap.values()); + return tagNames; } /** @@ -439,7 +484,7 @@ public List<TagName> getTagNamesInUseForUser(long dsObjId, String userName) thro */ public Map<String, TagName> getDisplayNamesToTagNamesMap() throws TskCoreException { Map<String, TagName> tagNames = new HashMap<>(); - for (TagName tagName : caseDb.getAllTagNames()) { + for (TagName tagName : getAllTagNames()) { tagNames.put(tagName.getDisplayName(), tagName); } return tagNames; @@ -521,13 +566,13 @@ public TagName addTagName(String displayName, String description, TagName.HTML_C public TagName addTagName(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown knownStatus) throws TagNameAlreadyExistsException, TskCoreException { synchronized (lock) { try { - TagName tagName = caseDb.addOrUpdateTagName(displayName, description, color, knownStatus); + TagName tagName = caseDb.getTaggingManager().addOrUpdateTagName(displayName, description, color, knownStatus); Set<TagNameDefinition> customTypes = TagNameDefinition.getTagNameDefinitions(); customTypes.add(new TagNameDefinition(displayName, description, color, knownStatus)); TagNameDefinition.setTagNameDefinitions(customTypes); return tagName; } catch (TskCoreException ex) { - List<TagName> existingTagNames = caseDb.getAllTagNames(); + List<TagName> existingTagNames = getAllTagNames(); for (TagName tagName : existingTagNames) { if (tagName.getDisplayName().equals(displayName)) { throw new TagNameAlreadyExistsException(); @@ -1039,5 +1084,4 @@ public boolean tagNameExists(String tagDisplayName) { @Override public void close() throws IOException { } - } diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/portablecase/PortableCaseReportModule.java b/Core/src/org/sleuthkit/autopsy/report/modules/portablecase/PortableCaseReportModule.java index 3efcd5125018e633d787f668e4c3d2098ea6479b..f18f05f61fdf206ca033a975ddebcab17fa8fd19 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/portablecase/PortableCaseReportModule.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/portablecase/PortableCaseReportModule.java @@ -340,7 +340,7 @@ public void generateReport(String reportPath, PortableCaseReportModuleSettings o progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingTags()); try { for (TagName tagName : tagNames) { - TagName newTagName = portableSkCase.addOrUpdateTagName(tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus()); + TagName newTagName = portableSkCase.getTaggingManager().addOrUpdateTagName(tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus()); oldTagNameToNewTagName.put(tagName, newTagName); } } catch (TskCoreException ex) { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryService.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryService.java index 4a8664eb6f7250c9ff49293217b19302bba511f0..897b50ae2bc52cd913419d14440cfe5384fbebb6 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryService.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryService.java @@ -149,7 +149,7 @@ public void closeCaseResources(CaseContext context) throws AutopsyServiceExcepti private void addProjetVicTagSet(Case currentCase) throws TskCoreException { List<TagName> tagNames = new ArrayList<>(); for (TagNameDefinition def : PROJECT_VIC_US_CATEGORIES) { - tagNames.add(currentCase.getSleuthkitCase().addOrUpdateTagName(def.getDisplayName(), def.getDescription(), def.getColor(), def.getKnownStatus())); + tagNames.add(currentCase.getSleuthkitCase().getTaggingManager().addOrUpdateTagName(def.getDisplayName(), def.getDescription(), def.getColor(), def.getKnownStatus())); } currentCase.getServices().getTagsManager().addTagSet(PROJECT_VIC_TAG_SET_NAME, tagNames); }