/* * Autopsy Forensic Browser * * Copyright 2013-16 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.imagegallery.datamodel; import com.google.common.eventbus.AsyncEventBus; import com.google.common.eventbus.EventBus; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.stream.Collectors; import javafx.scene.Node; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javax.annotation.Nonnull; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; /** * Manages Tags, Tagging, and the relationship between Categories and Tags in * the autopsy Db. Delegates some work to the backing {@link TagsManager}. */ @NbBundle.Messages({"DrawableTagsManager.followUp=Follow Up", "DrawableTagsManager.bookMark=Bookmark"}) public class DrawableTagsManager { private static final Logger LOGGER = Logger.getLogger(DrawableTagsManager.class.getName()); private static final String FOLLOW_UP = Bundle.DrawableTagsManager_followUp(); private static final String BOOKMARK = Bundle.DrawableTagsManager_bookMark(); private static Image FOLLOW_UP_IMAGE; private static Image BOOKMARK_IMAGE; public static String getFollowUpText() { return FOLLOW_UP; } public static String getBookmarkText() { return BOOKMARK; } final private Object autopsyTagsManagerLock = new Object(); private TagsManager autopsyTagsManager; /** * Used to distribute {@link TagsChangeEvent}s */ private final EventBus tagsEventBus = new AsyncEventBus( Executors.newSingleThreadExecutor( new BasicThreadFactory.Builder().namingPattern("Tags Event Bus").uncaughtExceptionHandler((Thread t, Throwable e) -> { //NON-NLS LOGGER.log(Level.SEVERE, "uncaught exception in event bus handler", e); //NON-NLS }).build() )); /** * The tag name corresponding to the "built-in" tag "Follow Up" */ private TagName followUpTagName; private TagName bookmarkTagName; public DrawableTagsManager(TagsManager autopsyTagsManager) { this.autopsyTagsManager = autopsyTagsManager; } /** * register an object to receive CategoryChangeEvents * * @param listener */ public void registerListener(Object listener) { tagsEventBus.register(listener); } /** * unregister an object from receiving CategoryChangeEvents * * @param listener */ public void unregisterListener(Object listener) { tagsEventBus.unregister(listener); } public void fireTagAddedEvent(ContentTagAddedEvent event) { tagsEventBus.post(event); } public void fireTagDeletedEvent(ContentTagDeletedEvent event) { tagsEventBus.post(event); } /** * assign a new TagsManager to back this one, ie when the current case * changes * * @param autopsyTagsManager */ public void setAutopsyTagsManager(TagsManager autopsyTagsManager) { synchronized (autopsyTagsManagerLock) { this.autopsyTagsManager = autopsyTagsManager; clearFollowUpTagName(); } } /** * Use when closing a case to make sure everything is re-initialized in the * next case. */ public void clearFollowUpTagName() { synchronized (autopsyTagsManagerLock) { followUpTagName = null; } } /** * get the (cached) follow up TagName * * @return * * @throws TskCoreException */ public TagName getFollowUpTagName() throws TskCoreException { synchronized (autopsyTagsManagerLock) { if (Objects.isNull(followUpTagName)) { followUpTagName = getTagName(FOLLOW_UP); } return followUpTagName; } } private Object getBookmarkTagName() throws TskCoreException { synchronized (autopsyTagsManagerLock) { if (Objects.isNull(bookmarkTagName)) { bookmarkTagName = getTagName(BOOKMARK); } return bookmarkTagName; } } /** * get all the TagNames that are not categories * * @return all the TagNames that are not categories, in alphabetical order * by displayName, or, an empty set if there was an exception * looking them up from the db. */ @Nonnull public List<TagName> getNonCategoryTagNames() { synchronized (autopsyTagsManagerLock) { try { return autopsyTagsManager.getAllTagNames().stream() .filter(CategoryManager::isNotCategoryTagName) .distinct().sorted() .collect(Collectors.toList()); } catch (TskCoreException | IllegalStateException ex) { LOGGER.log(Level.WARNING, "couldn't access case", ex); //NON-NLS } return Collections.emptyList(); } } /** * Gets content tags by content. * * @param content The content of interest. * * @return A list, possibly empty, of the tags that have been applied to the * content. * * @throws TskCoreException if there was an error reading from the db */ public List<ContentTag> getContentTags(Content content) throws TskCoreException { synchronized (autopsyTagsManagerLock) { return autopsyTagsManager.getContentTagsByContent(content); } } /** * Gets content tags by DrawableFile. * * @param drawable The DrawableFile of interest. * * @return A list, possibly empty, of the tags that have been applied to the * DrawableFile. * * @throws TskCoreException if there was an error reading from the db */ public List<ContentTag> getContentTags(DrawableFile drawable) throws TskCoreException { return getContentTags(drawable.getAbstractFile()); } public TagName getTagName(String displayName) throws TskCoreException { synchronized (autopsyTagsManagerLock) { try { TagName returnTagName = autopsyTagsManager.getDisplayNamesToTagNamesMap().get(displayName); if (returnTagName != null) { return returnTagName; } try { return autopsyTagsManager.addTagName(displayName); } catch (TagsManager.TagNameAlreadyExistsException ex) { returnTagName = autopsyTagsManager.getDisplayNamesToTagNamesMap().get(displayName); if (returnTagName != null) { return returnTagName; } throw new TskCoreException("Tag name exists but an error occured in retrieving it", ex); } } catch (NullPointerException | IllegalStateException ex) { LOGGER.log(Level.SEVERE, "Case was closed out from underneath", ex); //NON-NLS throw new TskCoreException("Case was closed out from underneath", ex); } } } public TagName getTagName(Category cat) { try { return getTagName(cat.getDisplayName()); } catch (TskCoreException ex) { return null; } } public ContentTag addContentTag(DrawableFile file, TagName tagName, String comment) throws TskCoreException { synchronized (autopsyTagsManagerLock) { return autopsyTagsManager.addContentTag(file.getAbstractFile(), tagName, comment); } } public List<ContentTag> getContentTagsByTagName(TagName t) throws TskCoreException { synchronized (autopsyTagsManagerLock) { return autopsyTagsManager.getContentTagsByTagName(t); } } public List<TagName> getAllTagNames() throws TskCoreException { synchronized (autopsyTagsManagerLock) { return autopsyTagsManager.getAllTagNames(); } } public List<TagName> getTagNamesInUse() throws TskCoreException { synchronized (autopsyTagsManagerLock) { return autopsyTagsManager.getTagNamesInUse(); } } public void deleteContentTag(ContentTag ct) throws TskCoreException { synchronized (autopsyTagsManagerLock) { autopsyTagsManager.deleteContentTag(ct); } } public Node getGraphic(TagName tagname) { try { if (tagname.equals(getFollowUpTagName())) { return new ImageView(getFollowUpImage()); } else if (tagname.equals(getBookmarkTagName())) { return new ImageView(getBookmarkImage()); } } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Failed to get \"Follow Up\" or \"Bookmark\"tag name from db.", ex); } return DrawableAttribute.TAGS.getGraphicForValue(tagname); } synchronized private static Image getFollowUpImage() { if (FOLLOW_UP_IMAGE == null) { FOLLOW_UP_IMAGE = new Image("/org/sleuthkit/autopsy/imagegallery/images/flag_red.png"); } return FOLLOW_UP_IMAGE; } synchronized private static Image getBookmarkImage() { if (BOOKMARK_IMAGE == null) { BOOKMARK_IMAGE = new Image("/org/sleuthkit/autopsy/images/star-bookmark-icon-16.png"); } return BOOKMARK_IMAGE; } }