Changes to tags management (partial)

This commit is contained in:
Richard Cordovano 2016-10-02 23:31:56 -04:00
parent 00a905533e
commit 6e60f9a978
8 changed files with 453 additions and 624 deletions

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-15 Basis Technology Corp. * Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -82,6 +82,8 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
// to be reworked. // to be reworked.
private class TagMenu extends JMenu { private class TagMenu extends JMenu {
private static final long serialVersionUID = 1L;
TagMenu() { TagMenu() {
super(getActionDisplayName()); super(getActionDisplayName());
@ -89,10 +91,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager();
Map<String, TagName> tagNamesMap = null; Map<String, TagName> tagNamesMap = null;
try { try {
tagNamesMap = new TreeMap<>(); tagNamesMap = new TreeMap<>(tagsManager.getDisplayNamesToTagNamesMap());
tagNamesMap.putAll(tagsManager.getUserTagNamesMap());
tagNamesMap.putAll(tagsManager.getPredefinedTagNamesMap());
tagNamesMap.putAll(tagsManager.getTagNamesInUseMap());
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
} }

View File

@ -118,9 +118,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
// not exist in the database). // not exist in the database).
TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager();
try { try {
tagNamesMap.putAll(tagsManager.getUserTagNamesMap()); tagNamesMap.putAll(tagsManager.getDisplayNamesToTagNamesMap());
tagNamesMap.putAll(tagsManager.getPredefinedTagNamesMap());
tagNamesMap.putAll(tagsManager.getTagNamesInUseMap());
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
Logger.getLogger(GetTagNameAndCommentDialog.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS Logger.getLogger(GetTagNameAndCommentDialog.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
} }

View File

@ -98,9 +98,7 @@ public class GetTagNameDialog extends JDialog {
// case the user chooses an existing tag name from the tag names table. // case the user chooses an existing tag name from the tag names table.
TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager();
try { try {
tagNamesMap.putAll(tagsManager.getUserTagNamesMap()); tagNamesMap.putAll(tagsManager.getDisplayNamesToTagNamesMap());
tagNamesMap.putAll(tagsManager.getPredefinedTagNamesMap());
tagNamesMap.putAll(tagsManager.getTagNamesInUseMap());
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
Logger.getLogger(GetTagNameDialog.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS Logger.getLogger(GetTagNameDialog.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
} }

View File

@ -138,8 +138,11 @@
<Component class="javax.swing.JList" name="tagNamesList"> <Component class="javax.swing.JList" name="tagNamesList">
<Properties> <Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor"> <Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="0"/> <StringArray count="1">
<StringItem index="0" value="TagType"/>
</StringArray>
</Property> </Property>
<Property name="selectionMode" type="int" value="0"/>
</Properties> </Properties>
<AuxValues> <AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;UserTagName&gt;"/> <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;UserTagName&gt;"/>

View File

@ -1,39 +1,35 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2016 Basis Technology Corp. * Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.casemodule.services; package org.sleuthkit.autopsy.casemodule.services;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.TreeSet;
import javax.swing.DefaultListModel; import javax.swing.DefaultListModel;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import org.netbeans.spi.options.OptionsPanelController; import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
/** /**
* A panel to allow the user to create new tag names or to delete tag names that * A panel to allow the user to create new tag names or to delete tag names that
@ -43,34 +39,26 @@ import org.sleuthkit.autopsy.coreutils.ModuleSettings;
*/ */
final class TagNamesSettingsPanel extends javax.swing.JPanel implements OptionsPanel { final class TagNamesSettingsPanel extends javax.swing.JPanel implements OptionsPanel {
private static final Logger logger = Logger.getLogger(TagNamesSettingsPanel.class.getName()); private static final long serialVersionUID = 1L;
private static final String TAGS_SETTINGS_NAME = "Tags"; //NON-NLS
private static final String TAG_NAMES_SETTING_KEY = "TagNames"; //NON-NLS
private static final String DEFAULT_DESCRIPTION = ""; private static final String DEFAULT_DESCRIPTION = "";
private static final String DEFAULT_COLOR_STRING = "NONE"; private static final String DEFAULT_COLOR_STRING = "NONE";
private final DefaultListModel<TagType> tagTypesListModel;
private DefaultListModel<UserTagName> tagNamesListModel; private final Set<TagType> tagTypes;
private List<UserTagName> tagNames;
/** /**
* Creates new form TagsManagerOptionsPanel * Creates new form TagsManagerOptionsPanel
*/ */
TagNamesSettingsPanel() { TagNamesSettingsPanel() {
tagTypesListModel = new DefaultListModel<>();
tagTypes = new TreeSet<>(TagType.getCustomTagTypes());
initComponents(); initComponents();
customizeComponents(); customizeComponents();
} }
private void customizeComponents() { private void customizeComponents() {
tagNamesListModel = new DefaultListModel<>(); tagNamesList.setModel(tagTypesListModel);
tagNamesList.setModel(tagNamesListModel); tagNamesList.addListSelectionListener((ListSelectionEvent event) -> {
tagNames = new ArrayList<>(); enableButtons();
tagNamesList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
enableButtons();
}
}); });
} }
@ -103,6 +91,12 @@ final class TagNamesSettingsPanel extends javax.swing.JPanel implements OptionsP
org.openide.awt.Mnemonics.setLocalizedText(tagNamesListLabel, org.openide.util.NbBundle.getMessage(TagNamesSettingsPanel.class, "TagNamesSettingsPanel.tagNamesListLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(tagNamesListLabel, org.openide.util.NbBundle.getMessage(TagNamesSettingsPanel.class, "TagNamesSettingsPanel.tagNamesListLabel.text")); // NOI18N
tagNamesList.setModel(new javax.swing.AbstractListModel<UserTagName>() {
String[] strings = { "TagType" };
public int getSize() { return strings.length; }
public Object getElementAt(int i) { return strings[i]; }
});
tagNamesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
jScrollPane1.setViewportView(tagNamesList); jScrollPane1.setViewportView(tagNamesList);
newTagNameButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/add-tag.png"))); // NOI18N newTagNameButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/add-tag.png"))); // NOI18N
@ -206,22 +200,22 @@ final class TagNamesSettingsPanel extends javax.swing.JPanel implements OptionsP
NewUserTagNameDialog.BUTTON_PRESSED result = dialog.getResult(); NewUserTagNameDialog.BUTTON_PRESSED result = dialog.getResult();
if (result == NewUserTagNameDialog.BUTTON_PRESSED.OK) { if (result == NewUserTagNameDialog.BUTTON_PRESSED.OK) {
String newTagDisplayName = dialog.getTagName(); String newTagDisplayName = dialog.getTagName();
UserTagName newTagName = new UserTagName(newTagDisplayName, DEFAULT_DESCRIPTION, DEFAULT_COLOR_STRING); TagType newTagType = new TagType(newTagDisplayName, DEFAULT_DESCRIPTION, DEFAULT_COLOR_STRING);
/* /*
* If tag name already exists, don't add the tag name. * If tag name already exists, don't add the tag name.
*/ */
if (tagNames.contains(newTagName)) { if (tagTypes.contains(newTagType)) {
JOptionPane.showMessageDialog(null, JOptionPane.showMessageDialog(null,
NbBundle.getMessage(TagNamesSettingsPanel.class, "TagNamesSettingsPanel.JOptionPane.tagNameAlreadyExists.message"), NbBundle.getMessage(TagNamesSettingsPanel.class, "TagNamesSettingsPanel.JOptionPane.tagNameAlreadyExists.message"),
NbBundle.getMessage(TagNamesSettingsPanel.class, "TagNamesSettingsPanel.JOptionPane.tagNameAlreadyExists.title"), NbBundle.getMessage(TagNamesSettingsPanel.class, "TagNamesSettingsPanel.JOptionPane.tagNameAlreadyExists.title"),
JOptionPane.INFORMATION_MESSAGE); JOptionPane.INFORMATION_MESSAGE);
} else { } else {
tagNames.add(newTagName); tagTypes.add(newTagType);
updateTagNamesListModel(); updateTagNamesListModel();
/* /*
* Set the selection to the tag name that was just added. * Set the selection to the tag name that was just added.
*/ */
int index = tagNames.indexOf(newTagName); int index = tagTypes.indexOf(newTagType);
tagNamesList.setSelectedIndex(index); tagNamesList.setSelectedIndex(index);
enableButtons(); enableButtons();
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
@ -230,8 +224,8 @@ final class TagNamesSettingsPanel extends javax.swing.JPanel implements OptionsP
}//GEN-LAST:event_newTagNameButtonActionPerformed }//GEN-LAST:event_newTagNameButtonActionPerformed
private void deleteTagNameButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteTagNameButtonActionPerformed private void deleteTagNameButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteTagNameButtonActionPerformed
UserTagName tagName = tagNamesList.getSelectedValue(); TagType tagName = tagNamesList.getSelectedValue();
tagNames.remove(tagName); tagTypes.remove(tagName);
updateTagNamesListModel(); updateTagNamesListModel();
enableButtons(); enableButtons();
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
@ -254,17 +248,28 @@ final class TagNamesSettingsPanel extends javax.swing.JPanel implements OptionsP
* Updates the tag names model for the tag names list component. * Updates the tag names model for the tag names list component.
*/ */
private void updateTagNamesListModel() { private void updateTagNamesListModel() {
tagNamesListModel.clear(); tagTypesListModel.clear();
Set<UserTagName> tagNameSet = new HashSet<>(); Set<TagType> tagNameSet = new HashSet<>();
tagNameSet.addAll(tagNames); tagNameSet.addAll(tagTypes);
tagNames.clear(); tagTypes.clear();
tagNames.addAll(tagNameSet); tagTypes.addAll(tagNameSet);
Collections.sort(tagNames); Collections.sort(tagTypes);
for (UserTagName tagName : tagNames) { for (TagType tagName : tagTypes) {
tagNamesListModel.addElement(tagName); tagTypesListModel.addElement(tagName);
} }
} }
/**
* Updates the tag names list component with tag names from the properties
* file.
*/
@Override
public void load() {
tagTypes = TagType.getCustomTagTypes();
updateTagNamesListModel();
enableButtons();
}
/** /**
* Stores tag name changes in the properties file, called when OK or Apply * Stores tag name changes in the properties file, called when OK or Apply
* is selected in the options panel. * is selected in the options panel.
@ -274,43 +279,15 @@ final class TagNamesSettingsPanel extends javax.swing.JPanel implements OptionsP
*/ */
@Override @Override
public void store() { public void store() {
StringBuilder setting = new StringBuilder(); TagType.setCustomTagTypes(tagTypes);
for (UserTagName tagName : tagNames) {
if (setting.length() != 0) {
setting.append(";");
}
setting.append(tagName.toSettingsFormat());
}
ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY, setting.toString());
if (Case.isCaseOpen()) {
Case.getCurrentCase().getServices().getTagsManager().storeNewUserTagNames(tagNames);
}
} }
/** /**
* Updates the tag names list component with tag names from the properties * Only enable the delete button when there is a tag type selected in the
* file. * tag types JList.
*/
@Override
public void load() {
String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY);
tagNames.clear();
if (null != setting && !setting.isEmpty()) {
List<String> tagNameTuples = Arrays.asList(setting.split(";"));
for (String tagNameTuple : tagNameTuples) {
String[] tagNameAttributes = tagNameTuple.split(",");
tagNames.add(new UserTagName(tagNameAttributes[0], tagNameAttributes[1], tagNameAttributes[2]));
}
}
updateTagNamesListModel();
enableButtons();
}
/**
* Only enable delete button when there is a tag name selected in the list.
*/ */
private void enableButtons() { private void enableButtons() {
boolean ruleIsSelected = tagNamesList.getSelectedIndex() != -1; deleteTagNameButton.setEnabled(tagNamesList.getSelectedIndex() != -1);
deleteTagNameButton.setEnabled(ruleIsSelected);
} }
} }

View File

@ -0,0 +1,177 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 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.services;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.annotation.concurrent.Immutable;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.datamodel.TagName;
/**
* A tag type definition consisting of a display name, description and color.
*/
@Immutable
final class TagType implements Comparable<TagType> {
private static final String TAGS_SETTINGS_NAME = "Tags"; //NON-NLS
private static final String TAG_NAMES_SETTING_KEY = "TagNames"; //NON-NLS
private final String displayName;
private final String description;
private final TagName.HTML_COLOR color;
/**
* Constructs a tag type definition consisting of a display name,
* description and color.
*
* @param displayName The display name for the tag type.
* @param description The dcescription of the tag type.
* @param color The color to associate with the tag type.
*/
TagType(String displayName, String description, TagName.HTML_COLOR color) {
this.displayName = displayName;
this.description = description;
this.color = color;
}
/**
* Gets the display name for a tag type.
*
* @return The display name.
*/
String getDisplayName() {
return displayName;
}
/**
* Gets the description of a tag type.
*
* @return The description.
*/
String getDescription() {
return description;
}
/**
* Gets the color associated with the tag type.
*
* @return The color.
*/
TagName.HTML_COLOR getColor() {
return color;
}
/**
* Compares this tag type with the specified tag type for order.
*
* @param other The tag type to which to compare this tag type.
*
* @return Negative integer, zero, or a positive integer to indicate that
* this tag type is less than, equal to, or greater than the
* specified tag type.
*/
@Override
public int compareTo(TagType other) {
return this.getDisplayName().toLowerCase().compareTo(other.getDisplayName().toLowerCase());
}
/**
* Returns a hash code value for this tag type.
*
* @return The has code.
*/
@Override
public int hashCode() {
int hash = 7;
hash = 83 * hash + Objects.hashCode(this.displayName);
return hash;
}
/**
* Indicates whether some other object is "equal to" this tag type.
*
* @param obj The object to test for equality.
*
* @return True or false.
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof TagType)) {
return false;
}
TagType thatTagName = (TagType) obj;
return this.getDisplayName().equals(thatTagName.getDisplayName());
}
/**
* A string representation of this tag type.
*
* @return The display name of the tag type.
*/
@Override
public String toString() {
return displayName;
}
/**
* @return A string representation of the tag name in the format that is
* used by the properties file.
*/
private String toSettingsFormat() {
return displayName + "," + description + "," + color.name();
}
/**
* Gets the custom tag types for the current user.
*
* @return A set of tag type objects.
*/
static synchronized Set<TagType> getCustomTagTypes() {
Set<TagType> tagTypes = new HashSet<>();
String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY);
if (null != setting && !setting.isEmpty()) {
List<String> tagNameTuples = Arrays.asList(setting.split(";"));
for (String tagNameTuple : tagNameTuples) {
String[] tagNameAttributes = tagNameTuple.split(",");
tagTypes.add(new TagType(tagNameAttributes[0], tagNameAttributes[1], TagName.HTML_COLOR.valueOf(tagNameAttributes[2])));
}
}
return tagTypes;
}
/**
* Sets the custom tag types for the current user.
*
* @param tagTypes A set of tag type objects.
*/
static synchronized void setCustomTagTypes(Set<TagType> tagTypes) {
StringBuilder setting = new StringBuilder();
for (TagType tagType : tagTypes) {
if (setting.length() != 0) {
setting.append(";");
}
setting.append(tagType.toSettingsFormat());
}
ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY, setting.toString());
}
}

View File

@ -1,37 +1,32 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2016 Basis Technology Corp. * Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.casemodule.services; package org.sleuthkit.autopsy.casemodule.services;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.BlackboardArtifactTag;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
@ -41,22 +36,17 @@ import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* A per case Autopsy service that manages the creation, updating, and deletion * A per case Autopsy service that manages the addition of content and artifact
* of tags applied to content and blackboard artifacts by users. * tags to the case database.
*/ */
public class TagsManager implements Closeable { public class TagsManager implements Closeable {
private static final Logger logger = Logger.getLogger(TagsManager.class.getName()); private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName());
private static final String TAGS_SETTINGS_NAME = "Tags"; //NON-NLS private final SleuthkitCase caseDb;
private static final String TAG_NAMES_SETTING_KEY = "TagNames"; //NON-NLS
private SleuthkitCase caseDb;
private final HashMap<String, TagName> uniqueTagNames = new HashMap<>();
private boolean tagNamesLoaded = false;
/** /**
* Constructs a per case Autopsy service that manages the creation, * Constructs a per case Autopsy service that manages the addition of
* updating, and deletion of tags applied to content and blackboard * content and artifact tags to the case database.
* artifacts by users.
* *
* @param caseDb The case database. * @param caseDb The case database.
*/ */
@ -67,205 +57,148 @@ public class TagsManager implements Closeable {
/** /**
* Gets a list of all tag names currently in the case database. * Gets a list of all tag names currently in the case database.
* *
* @return A list, possibly empty, of TagName data transfer objects (DTOs). * @return A list, possibly empty, of TagName objects.
* *
* @throws TskCoreException If there is an error reading from the case * @throws TskCoreException If there is an error querying the case database.
* database.
*/ */
public synchronized List<TagName> getAllTagNames() throws TskCoreException { public List<TagName> getAllTagNames() throws TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
lazyLoadExistingTagNames();
return caseDb.getAllTagNames(); return caseDb.getAllTagNames();
} }
/** /**
* Gets a mapping of user tag name display names to TagName DTOs if they * Gets a list of all tag names currently in use in the case database for
* have been added to the database. Otherwise, the display name maps to * tagging content or artifacts.
* null.
* *
* @return A map of String display name to TagName DTO, TagName may be null * @return A list, possibly empty, of TagName objects.
*
* @throws TskCoreException If there is an error querying the case database.
*/ */
public synchronized Map<String, TagName> getUserTagNamesMap() { public List<TagName> getTagNamesInUse() throws TskCoreException {
lazyLoadExistingTagNames();
Map<String, TagName> tagNamesMap = new HashMap<>();
String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY);
if (null != setting && !setting.isEmpty()) {
List<String> tagNameTuples = Arrays.asList(setting.split(";"));
for (String tagNameTuple : tagNameTuples) {
String[] tagNameAttributes = tagNameTuple.split(",");
tagNamesMap.put(tagNameAttributes[0], uniqueTagNames.get(tagNameAttributes[0]));
}
}
return tagNamesMap;
}
/**
* Gets a mapping of predefined tag names to their TagName DTOs. Currently
* only for the bookmark tag.
*
* @return A map of String display name to TagName DTO
*/
public synchronized Map<String, TagName> getPredefinedTagNamesMap() {
Map<String, TagName> tagNamesMap = new HashMap<>();
TagName bookmarkTagName = uniqueTagNames.get(NbBundle.getMessage(this.getClass(), "TagsManager.predefTagNames.bookmark.text"));
tagNamesMap.put(NbBundle.getMessage(this.getClass(), "TagsManager.predefTagNames.bookmark.text"), bookmarkTagName);
return tagNamesMap;
}
/**
* Gets a mapping of the display names of predefined tag names and tag names
* that are in use to their TagName DTOs.
*
* @return A map of String display name to TagName DTO
*
* @throws TskCoreException If there is an error reading from the case
* database.
*/
public synchronized Map<String, TagName> getTagNamesInUseMap() throws TskCoreException {
List<TagName> tagNames = getTagNamesInUse();
Map<String, TagName> tagNamesMap = new HashMap<>();
for (TagName tagName : tagNames) {
tagNamesMap.put(tagName.getDisplayName(), tagName);
}
return tagNamesMap;
}
/**
* Gets a list of all tag names currently in use for tagging content or
* artifacts.
*
* @return A list, possibly empty, of TagName data transfer objects (DTOs).
*
* @throws TskCoreException If there is an error reading from the case
* database.
*/
public synchronized List<TagName> getTagNamesInUse() throws TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
lazyLoadExistingTagNames();
return caseDb.getTagNamesInUse(); return caseDb.getTagNamesInUse();
} }
/** /**
* Checks whether a tag name with a given display name exists. * Gets a map of tag display names to tag name entries in the case database.
* It has keys for the display names of the standard tag types, the current
* user's custom tag types, and the tags in the case database. The value for
* a given key will be null if the corresponding tag type is defined, but a
* tag name entry has not yet added to the case database. In that case,
* addTagName may be called to add the tag name entry.
* *
* @param tagDisplayName The display name to check. * @return A map of tag display names to possibly null TagName object
* references.
* *
* @return True or false. * @throws TskCoreException if there is an error querying the case database.
*/ */
public synchronized boolean tagNameExists(String tagDisplayName) { public synchronized Map<String, TagName> getDisplayNamesToTagNamesMap() throws TskCoreException {
lazyLoadExistingTagNames(); /**
return uniqueTagNames.containsKey(tagDisplayName) && * Order is important here. The keys (display names) for the standard
(uniqueTagNames.get(tagDisplayName) != null); * tag types and current user's custom tag types are added to the map
* first, with null TagName values. If tag name entries exist for those
* keys, loading of the tag names from the database supplies the missing
* values.
*
* Note that creating the map on demand increases the probability that
* the display names of newly added custom tag types and the display
* names of tags added to a multi-user case by other users appear in the
* map.
*/
Map<String, TagName> tagNames = new HashMap<>();
tagNames.put("TagsManager.predefTagNames.bookmark.text", null);
Set<TagType> customTypes = TagType.getCustomTagTypes();
for (TagType tagType : customTypes) {
tagNames.put(tagType.getDisplayName(), null);
}
for (TagName tagName : caseDb.getAllTagNames()) {
tagNames.put(tagName.getDisplayName(), tagName);
}
return new HashMap<>(tagNames);
} }
/** /**
* Adds a new tag name to the current case and to the tags settings. * Adds a tag name entry to the case database and adds a corresponding
* tag type to the current user's custom tag types.
* *
* @param displayName The display name for the new tag name. * @param displayName The display name for the new tag type.
* *
* @return A TagName data transfer object (DTO) representing the new tag * @return A TagName representing the tag name database entry that can be
* name. * used to add instances of the tag type to the case database.
* *
* @throws TagNameAlreadyExistsException If the tag name would be a * @throws TagNameAlreadyExistsException If the tag name already exists in
* duplicate. * the case database.
* @throws TskCoreException If there is an error adding the tag * @throws TskCoreException If there is an error adding the tag
* to the case database. * name to the case database.
*/ */
public TagName addTagName(String displayName) throws TagNameAlreadyExistsException, TskCoreException { public synchronized TagName addTagName(String displayName) throws TagNameAlreadyExistsException, TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
return addTagName(displayName, "", TagName.HTML_COLOR.NONE); return addTagName(displayName, "", TagName.HTML_COLOR.NONE);
} }
/** /**
* Adds a new tag name to the current case and to the tags settings. * Adds a tag name entry to the case database and adds a corresponding
* tag type to the current user's custom tag types.
* *
* @param displayName The display name for the new tag name. * @param displayName The display name for the new tag type.
* @param description The description for the new tag name. * @param description The description for the new tag type.
* *
* @return A TagName data transfer object (DTO) representing the new tag * @return A TagName object that can be used to add instances of the tag
* name. * type to the case database.
* *
* @throws TagNameAlreadyExistsException If the tag name would be a * @throws TagNameAlreadyExistsException If the tag name already exists in
* duplicate. * the case database.
* @throws TskCoreException If there is an error adding the tag * @throws TskCoreException If there is an error adding the tag
* to the case database. * name to the case database.
*/ */
public TagName addTagName(String displayName, String description) throws TagNameAlreadyExistsException, TskCoreException { public synchronized TagName addTagName(String displayName, String description) throws TagNameAlreadyExistsException, TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
return addTagName(displayName, description, TagName.HTML_COLOR.NONE); return addTagName(displayName, description, TagName.HTML_COLOR.NONE);
} }
/** /**
* Adds a new tag name to the current case and to the tags settings. * Adds a tag name entry to the case database and adds a corresponding
* tag type to the current user's custom tag types.
* *
* @param displayName The display name for the new tag name. * @param displayName The display name for the new tag type.
* @param description The description for the new tag name. * @param description The description for the new tag type.
* @param color The HTML color to associate with the new tag name. * @param color The color to associate with the new tag type.
* *
* @return A TagName data transfer object (DTO) representing the new tag * @return A TagName object that can be used to add instances of the tag
* name. * type to the case database.
* *
* @throws TagNameAlreadyExistsException If the tag name would be a * @throws TagNameAlreadyExistsException If the tag name already exists.
* duplicate.
* @throws TskCoreException If there is an error adding the tag * @throws TskCoreException If there is an error adding the tag
* to the case database. * name to the case database.
*/ */
public synchronized TagName addTagName(String displayName, String description, TagName.HTML_COLOR color) throws TagNameAlreadyExistsException, TskCoreException { public synchronized TagName addTagName(String displayName, String description, TagName.HTML_COLOR color) throws TagNameAlreadyExistsException, TskCoreException {
if (null == caseDb) { try {
throw new TskCoreException("Tags manager has been closed"); TagName tagName = caseDb.addTagName(displayName, description, color);
} Set<TagType> customTypes = TagType.getCustomTagTypes();
customTypes.add(new TagType(displayName, description, color));
lazyLoadExistingTagNames(); TagType.setCustomTagTypes(customTypes);
return tagName;
/* } catch (TskCoreException ex) {
* It is possible user is trying to add back a tag after having deleted List<TagName> existingTagNames = caseDb.getAllTagNames();
* it. It is also possible a user tag name was never added to the for (TagName tagName : existingTagNames) {
* database. if (tagName.getDisplayName().equals(displayName)) {
*/
if (uniqueTagNames.containsKey(displayName)) {
if (uniqueTagNames.get(displayName) != null) {
TagName existingTagName = uniqueTagNames.get(displayName);
long count = getContentTagsCountByTagName(existingTagName) + getBlackboardArtifactTagsCountByTagName(existingTagName);
if (count == 0) {
addNewTagNameToTagsSettings(existingTagName);
return existingTagName;
} else {
throw new TagNameAlreadyExistsException(); throw new TagNameAlreadyExistsException();
} }
} }
throw ex;
} }
TagName newTagName = caseDb.addTagName(displayName, description, color);
uniqueTagNames.put(newTagName.getDisplayName(), newTagName);
addNewTagNameToTagsSettings(newTagName);
return newTagName;
} }
/** /**
* Tags a content object. * Tags a content object.
* *
* @param content The content to tag. * @param content The content to tag.
* @param tagName The name to use for the tag. * @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* *
* @return A ContentTag data transfer object (DTO) representing the new tag. * @return A ContentTag object representing the new tag.
* *
* @throws TskCoreException If there is an error adding the tag to the case * @throws TskCoreException If there is an error adding the tag to the case
* database. * database.
*/ */
public ContentTag addContentTag(Content content, TagName tagName) throws TskCoreException { public ContentTag addContentTag(Content content, TagName tagName) throws TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
return addContentTag(content, tagName, "", -1, -1); return addContentTag(content, tagName, "", -1, -1);
} }
@ -273,18 +206,17 @@ public class TagsManager implements Closeable {
* Tags a content object. * Tags a content object.
* *
* @param content The content to tag. * @param content The content to tag.
* @param tagName The name to use for the tag. * @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* @param comment A comment to store with the tag. * @param comment A comment to store with the tag.
* *
* @return A ContentTag data transfer object (DTO) representing the new tag. * @return A ContentTag object representing the new tag.
* *
* @throws TskCoreException If there is an error adding the tag to the case * @throws TskCoreException If there is an error adding the tag to the case
* database. * database.
*/ */
public ContentTag addContentTag(Content content, TagName tagName, String comment) throws TskCoreException { public ContentTag addContentTag(Content content, TagName tagName, String comment) throws TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
return addContentTag(content, tagName, comment, -1, -1); return addContentTag(content, tagName, comment, -1, -1);
} }
@ -292,56 +224,25 @@ public class TagsManager implements Closeable {
* Tags a content object or a section of a content object. * Tags a content object or a section of a content object.
* *
* @param content The content to tag. * @param content The content to tag.
* @param tagName The name to use for the tag. * @param tagName The representation of the desired tag type in the
* case database, which can be obtained by calling
* getTagNames and/or addTagName.
* @param comment A comment to store with the tag. * @param comment A comment to store with the tag.
* @param beginByteOffset Designates the beginning of a tagged section. * @param beginByteOffset Designates the beginning of a tagged section.
* @param endByteOffset Designates the end of a tagged section. * @param endByteOffset Designates the end of a tagged section.
* *
* @return A ContentTag data transfer object (DTO) representing the new tag. * @return A ContentTag object representing the new tag.
* *
* @throws IllegalArgumentException If a requested byte offset is out of * @throws TskCoreException If there is an error adding the tag to the case
* range. * database.
* @throws TskCoreException If there is an error adding the tag to
* the case database.
*/ */
public ContentTag addContentTag(Content content, TagName tagName, String comment, long beginByteOffset, long endByteOffset) throws IllegalArgumentException, TskCoreException { public ContentTag addContentTag(Content content, TagName tagName, String comment, long beginByteOffset, long endByteOffset) throws TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
ContentTag tag; ContentTag tag;
synchronized (this) { tag = caseDb.addContentTag(content, tagName, comment, beginByteOffset, endByteOffset);
lazyLoadExistingTagNames();
if (null == comment) {
throw new IllegalArgumentException("Passed null comment argument");
}
if (beginByteOffset >= 0 && endByteOffset >= 1) {
if (beginByteOffset > content.getSize() - 1) {
throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(),
"TagsManager.addContentTag.exception.beginByteOffsetOOR.msg",
beginByteOffset, content.getSize() - 1));
}
if (endByteOffset > content.getSize() - 1) {
throw new IllegalArgumentException(
NbBundle.getMessage(this.getClass(), "TagsManager.addContentTag.exception.endByteOffsetOOR.msg",
endByteOffset, content.getSize() - 1));
}
if (endByteOffset < beginByteOffset) {
throw new IllegalArgumentException(
NbBundle.getMessage(this.getClass(), "TagsManager.addContentTag.exception.endLTbegin.msg"));
}
}
tag = caseDb.addContentTag(content, tagName, comment, beginByteOffset, endByteOffset);
}
try { try {
Case.getCurrentCase().notifyContentTagAdded(tag); Case.getCurrentCase().notifyContentTagAdded(tag);
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
logger.log(Level.SEVERE, NbBundle.getMessage(TagsManager.class, "TagsManager.addContentTag.noCaseWarning"), ex); LOGGER.log(Level.SEVERE, "Added a tag to a closed case", ex);
} }
return tag; return tag;
} }
@ -355,18 +256,11 @@ public class TagsManager implements Closeable {
* case database. * case database.
*/ */
public void deleteContentTag(ContentTag tag) throws TskCoreException { public void deleteContentTag(ContentTag tag) throws TskCoreException {
if (null == caseDb) { caseDb.deleteContentTag(tag);
throw new TskCoreException("Tags manager has been closed");
}
synchronized (this) {
lazyLoadExistingTagNames();
caseDb.deleteContentTag(tag);
}
try { try {
Case.getCurrentCase().notifyContentTagDeleted(tag); Case.getCurrentCase().notifyContentTagDeleted(tag);
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
logger.log(Level.SEVERE, NbBundle.getMessage(TagsManager.class, "TagsManager.deleteContentTag.noCaseWarning"), ex); LOGGER.log(Level.SEVERE, "Deleted a tag from a closed case", ex);
} }
} }
@ -379,17 +273,15 @@ public class TagsManager implements Closeable {
* case database. * case database.
*/ */
public synchronized List<ContentTag> getAllContentTags() throws TskCoreException { public synchronized List<ContentTag> getAllContentTags() throws TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
lazyLoadExistingTagNames();
return caseDb.getAllContentTags(); return caseDb.getAllContentTags();
} }
/** /**
* Gets content tags count by tag name. * Gets content tags count by tag name.
* *
* @param tagName The tag name of interest. * @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* *
* @return A count of the content tags with the specified tag name. * @return A count of the content tags with the specified tag name.
* *
@ -397,29 +289,21 @@ public class TagsManager implements Closeable {
* the case database. * the case database.
*/ */
public synchronized long getContentTagsCountByTagName(TagName tagName) throws TskCoreException { public synchronized long getContentTagsCountByTagName(TagName tagName) throws TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
lazyLoadExistingTagNames();
return caseDb.getContentTagsCountByTagName(tagName); return caseDb.getContentTagsCountByTagName(tagName);
} }
/** /**
* Gets a content tag by tag id. * Gets a content tag by tag id.
* *
* @param tagID The tag id of interest. * @param tagId The tag id of interest.
* *
* @return The content tag with the specified tag id. * @return The content tag with the specified tag id.
* *
* @throws TskCoreException If there is an error getting the tag from the * @throws TskCoreException If there is an error getting the tag from the
* case database. * case database.
*/ */
public synchronized ContentTag getContentTagByTagID(long tagID) throws TskCoreException { public synchronized ContentTag getContentTagByTagID(long tagId) throws TskCoreException {
if (null == caseDb) { return caseDb.getContentTagByID(tagId);
throw new TskCoreException("Tags manager has been closed");
}
lazyLoadExistingTagNames();
return caseDb.getContentTagByID(tagID);
} }
/** /**
@ -434,10 +318,6 @@ public class TagsManager implements Closeable {
* case database. * case database.
*/ */
public synchronized List<ContentTag> getContentTagsByTagName(TagName tagName) throws TskCoreException { public synchronized List<ContentTag> getContentTagsByTagName(TagName tagName) throws TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
lazyLoadExistingTagNames();
return caseDb.getContentTagsByTagName(tagName); return caseDb.getContentTagsByTagName(tagName);
} }
@ -447,172 +327,136 @@ public class TagsManager implements Closeable {
* @param content The content of interest. * @param content The content of interest.
* *
* @return A list, possibly empty, of the tags that have been applied to the * @return A list, possibly empty, of the tags that have been applied to the
* artifact. * content.
* *
* @throws TskCoreException If there is an error getting the tags from the * @throws TskCoreException If there is an error getting the tags from the
* case database. * case database.
*/ */
public synchronized List<ContentTag> getContentTagsByContent(Content content) throws TskCoreException { public synchronized List<ContentTag> getContentTagsByContent(Content content) throws TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
lazyLoadExistingTagNames();
return caseDb.getContentTagsByContent(content); return caseDb.getContentTagsByContent(content);
} }
/** /**
* Tags a blackboard artifact object. * Tags an artifact.
* *
* @param artifact The blackboard artifact to tag. * @param artifact The artifact to tag.
* @param tagName The name to use for the tag. * @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* *
* @return A BlackboardArtifactTag data transfer object (DTO) representing * @return A BlackboardArtifactTag object representing the new tag.
* the new tag.
* *
* @throws TskCoreException If there is an error adding the tag to the case * @throws TskCoreException If there is an error adding the tag to the case
* database. * database.
*/ */
public BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName) throws TskCoreException { public synchronized BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName) throws TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
return addBlackboardArtifactTag(artifact, tagName, ""); return addBlackboardArtifactTag(artifact, tagName, "");
} }
/** /**
* Tags a blackboard artifact object. * Tags an artifact.
* *
* @param artifact The blackboard artifact to tag. * @param artifact The artifact to tag.
* @param tagName The name to use for the tag. * @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* @param comment A comment to store with the tag. * @param comment A comment to store with the tag.
* *
* @return A BlackboardArtifactTag data transfer object (DTO) representing * @return A BlackboardArtifactTag object representing the new tag.
* the new tag.
* *
* @throws TskCoreException If there is an error adding the tag to the case * @throws TskCoreException If there is an error adding the tag to the case
* database. * database.
*/ */
public BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName, String comment) throws TskCoreException { public synchronized BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName, String comment) throws TskCoreException {
if (null == caseDb) { BlackboardArtifactTag tag = caseDb.addBlackboardArtifactTag(artifact, tagName, comment);
throw new TskCoreException("Tags manager has been closed");
}
BlackboardArtifactTag tag;
synchronized (this) {
lazyLoadExistingTagNames();
if (null == comment) {
throw new IllegalArgumentException("Passed null comment argument");
}
tag = caseDb.addBlackboardArtifactTag(artifact, tagName, comment);
}
try { try {
Case.getCurrentCase().notifyBlackBoardArtifactTagAdded(tag); Case.getCurrentCase().notifyBlackBoardArtifactTagAdded(tag);
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
logger.log(Level.SEVERE, NbBundle.getMessage(TagsManager.class, "TagsManager.addBlackboardArtifactTag.noCaseWarning"), ex); LOGGER.log(Level.SEVERE, "Added a tag to a closed case", ex);
} }
return tag; return tag;
} }
/** /**
* Deletes a blackboard artifact tag. * Deletes an artifact tag.
* *
* @param tag The tag to delete. * @param tag The tag to delete.
* *
* @throws TskCoreException If there is an error deleting the tag from the * @throws TskCoreException If there is an error deleting the tag from the
* case database. * case database.
*/ */
public void deleteBlackboardArtifactTag(BlackboardArtifactTag tag) throws TskCoreException { public synchronized void deleteBlackboardArtifactTag(BlackboardArtifactTag tag) throws TskCoreException {
if (null == caseDb) { caseDb.deleteBlackboardArtifactTag(tag);
throw new TskCoreException("Tags manager has been closed");
}
synchronized (this) {
lazyLoadExistingTagNames();
caseDb.deleteBlackboardArtifactTag(tag);
}
try { try {
Case.getCurrentCase().notifyBlackBoardArtifactTagDeleted(tag); Case.getCurrentCase().notifyBlackBoardArtifactTagDeleted(tag);
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
logger.log(Level.WARNING, NbBundle.getMessage(TagsManager.class, "TagsManager.deleteBlackboardArtifactTag.noCaseWarning"), ex); LOGGER.log(Level.SEVERE, "Deleted a tag from a closed case", ex);
} }
} }
/** /**
* Gets all blackboard artifact tags for the current case. * Gets all artifact tags for the current case.
* *
* @return A list, possibly empty, of blackboard artifact tags. * @return A list, possibly empty, of artifact tags.
* *
* @throws TskCoreException If there is an error getting the tags from the * @throws TskCoreException If there is an error getting the tags from the
* case database. * case database.
*/ */
public synchronized List<BlackboardArtifactTag> getAllBlackboardArtifactTags() throws TskCoreException { public synchronized List<BlackboardArtifactTag> getAllBlackboardArtifactTags() throws TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
lazyLoadExistingTagNames();
return caseDb.getAllBlackboardArtifactTags(); return caseDb.getAllBlackboardArtifactTags();
} }
/** /**
* Gets blackboard artifact tags count by tag name. * Gets an artifact tags count by tag name.
* *
* @param tagName The tag name of interest. * @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* *
* @return A count of the blackboard artifact tags with the specified tag * @return A count of the artifact tags with the specified tag name.
* name.
* *
* @throws TskCoreException If there is an error getting the tags count from * @throws TskCoreException If there is an error getting the tags count from
* the case database. * the case database.
*/ */
public synchronized long getBlackboardArtifactTagsCountByTagName(TagName tagName) throws TskCoreException { public synchronized long getBlackboardArtifactTagsCountByTagName(TagName tagName) throws TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
lazyLoadExistingTagNames();
return caseDb.getBlackboardArtifactTagsCountByTagName(tagName); return caseDb.getBlackboardArtifactTagsCountByTagName(tagName);
} }
/** /**
* Gets a blackboard artifact tag by tag id. * Gets an artifact tag by tag id.
* *
* @param tagID The tag id of interest. * @param tagId The tag id of interest.
* *
* @return the blackboard artifact tag with the specified tag id. * @return The artifact tag with the specified tag id.
* *
* @throws TskCoreException If there is an error getting the tag from the * @throws TskCoreException If there is an error getting the tag from the
* case database. * case database.
*/ */
public synchronized BlackboardArtifactTag getBlackboardArtifactTagByTagID(long tagID) throws TskCoreException { public synchronized BlackboardArtifactTag getBlackboardArtifactTagByTagID(long tagId) throws TskCoreException {
if (null == caseDb) { return caseDb.getBlackboardArtifactTagByID(tagId);
throw new TskCoreException("Tags manager has been closed");
}
lazyLoadExistingTagNames();
return caseDb.getBlackboardArtifactTagByID(tagID);
} }
/** /**
* Gets blackboard artifact tags by tag name. * Gets artifact tags by tag name.
* *
* @param tagName The tag name of interest. * @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* *
* @return A list, possibly empty, of the blackboard artifact tags with the * @return A list, possibly empty, of the artifact tags with the specified
* specified tag name. * tag name.
* *
* @throws TskCoreException If there is an error getting the tags from the * @throws TskCoreException If there is an error getting the tags from the
* case database. * case database.
*/ */
public synchronized List<BlackboardArtifactTag> getBlackboardArtifactTagsByTagName(TagName tagName) throws TskCoreException { public synchronized List<BlackboardArtifactTag> getBlackboardArtifactTagsByTagName(TagName tagName) throws TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
lazyLoadExistingTagNames();
return caseDb.getBlackboardArtifactTagsByTagName(tagName); return caseDb.getBlackboardArtifactTagsByTagName(tagName);
} }
/** /**
* Gets blackboard artifact tags for a particular blackboard artifact. * Gets artifact tags for a particular artifact.
* *
* @param artifact The blackboard artifact of interest. * @param artifact The artifact of interest.
* *
* @return A list, possibly empty, of the tags that have been applied to the * @return A list, possibly empty, of the tags that have been applied to the
* artifact. * artifact.
@ -621,129 +465,15 @@ public class TagsManager implements Closeable {
* case database. * case database.
*/ */
public synchronized List<BlackboardArtifactTag> getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact) throws TskCoreException { public synchronized List<BlackboardArtifactTag> getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact) throws TskCoreException {
if (null == caseDb) {
throw new TskCoreException("Tags manager has been closed");
}
lazyLoadExistingTagNames();
return caseDb.getBlackboardArtifactTagsByArtifact(artifact); return caseDb.getBlackboardArtifactTagsByArtifact(artifact);
} }
/**
* Closes the tags manager, saving the available tag names to secondary
* storage.
*
* @throws IOException If there is a problem closing the tags manager.
* @deprecated Tags manager clients should not close the tags manager.
*/
@Override
@Deprecated
public synchronized void close() throws IOException {
caseDb = null;
}
/**
* Populates the tag names collection and the tag names table in the case
* database with the existing tag names from all sources.
*/
private void lazyLoadExistingTagNames() {
if (!tagNamesLoaded) {
addTagNamesFromCurrentCase();
addPredefinedTagNames();
addTagNamesFromTagsSettings();
tagNamesLoaded = true;
} else {
// Reload case db tag names in case another user has added some.
addTagNamesFromCurrentCase();
}
}
/**
* Adds any tag names that are in the case database to the tag names
* collection.
*/
private void addTagNamesFromCurrentCase() {
try {
List<TagName> currentTagNames = caseDb.getAllTagNames();
for (TagName tagName : currentTagNames) {
uniqueTagNames.put(tagName.getDisplayName(), tagName);
}
} catch (TskCoreException ex) {
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag types from the current case", ex); //NON-NLS
}
}
/**
* Adds any tag names that are in the properties file to the tag names
* collection and to the case database. The properties file is used to make
* it possible to use tag names across cases.
*/
private void addTagNamesFromTagsSettings() {
String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY);
if (null != setting && !setting.isEmpty()) {
// Read the tag name setting and break it into tag name tuples.
List<String> tagNameTuples = Arrays.asList(setting.split(";"));
// Parse each tuple and add the tag names to the current case, one
// at a time to gracefully discard any duplicates or corrupt tuples.
for (String tagNameTuple : tagNameTuples) {
String[] tagNameAttributes = tagNameTuple.split(",");
if (!uniqueTagNames.containsKey(tagNameAttributes[0])) {
uniqueTagNames.put(tagNameAttributes[0], null);
}
}
}
}
/**
* Adds the standard tag names to the tag names collection.
*/
private void addPredefinedTagNames() {
if (!uniqueTagNames.containsKey(NbBundle.getMessage(this.getClass(), "TagsManager.predefTagNames.bookmark.text"))) {
try {
TagName tagName = caseDb.addTagName(
NbBundle.getMessage(this.getClass(), "TagsManager.predefTagNames.bookmark.text"), "", TagName.HTML_COLOR.NONE);
uniqueTagNames.put(tagName.getDisplayName(), tagName);
} catch (TskCoreException ex) {
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to add standard 'Bookmark' tag name to case database", ex); //NON-NLS
}
}
}
/**
* Adds any user defined tag name to the case db and also to uniqueTagNames,
* to allow user tag names to be displayed while tagging.
*
* @param userTagNames a List of UserTagName objects to be potentially added
*/
void storeNewUserTagNames(List<UserTagName> userTagNames) {
lazyLoadExistingTagNames();
for (UserTagName utn : userTagNames) {
if (!uniqueTagNames.containsKey(utn.getDisplayName())) {
uniqueTagNames.put(utn.getDisplayName(), null);
}
}
}
/**
* Adds a new tag name to the settings file, used when user creates a new
* tag name.
*/
private void addNewTagNameToTagsSettings(TagName tagName) {
String setting = ModuleSettings.getConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY);
if (setting == null || setting.isEmpty()) {
setting = "";
} else {
setting += ";";
}
setting += tagName.getDisplayName() + "," + tagName.getDescription() + "," + tagName.getColor().toString();
ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY, setting);
}
/** /**
* Returns true if the tag display name contains an illegal character. Used * Returns true if the tag display name contains an illegal character. Used
* after a tag display name is retrieved from user input. * after a tag display name is retrieved from user input.
* *
* @param content Display name of the tag being added. * @param content Display name of the tag being added.
*
* @return boolean indicating whether the name has an invalid character. * @return boolean indicating whether the name has an invalid character.
*/ */
public static boolean containsIllegalCharacters(String content) { public static boolean containsIllegalCharacters(String content) {
@ -757,6 +487,7 @@ public class TagsManager implements Closeable {
|| content.contains("|") || content.contains("|")
|| content.contains(",") || content.contains(",")
|| content.contains(";")); || content.contains(";"));
} }
/** /**
@ -767,4 +498,36 @@ public class TagsManager implements Closeable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
} }
/**
* Checks whether a tag name with a given display name exists in the case
* database.
*
* @param tagDisplayName The display name.
*
* @return True or false.
*
* @deprecated Not reliable for multi-user cases.
*/
@Deprecated
public synchronized boolean tagNameExists(String tagDisplayName) {
try {
Map<String, TagName> tagNames = getDisplayNamesToTagNamesMap();
return tagNames.containsKey(tagDisplayName) && (tagNames.get(tagDisplayName) != null);
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error querying case database for tag names", ex);
return false;
}
}
/**
* Closes the tags manager.
*
* @throws IOException If there is a problem closing the tags manager.
* @deprecated Tags manager clients should not close the tags manager.
*/
@Override
@Deprecated
public synchronized void close() throws IOException {
}
} }

View File

@ -1,86 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 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.services;
import java.util.Objects;
/**
* Because the DTO TagName constructor can not be called outside of its class
* package, UserTagName is used to keep track of user tag names while
* preserving properties that will potentially be implemented in the future
* (tag name description and tag name color).
*/
class UserTagName implements Comparable<UserTagName> {
private final String displayName;
private final String description;
private final String colorName;
UserTagName(String displayName, String description, String colorName) {
this.displayName = displayName;
this.description = description;
this.colorName = colorName;
}
String getDisplayName() {
return displayName;
}
String getDescription() {
return description;
}
String getColorName() {
return colorName;
}
@Override
public int compareTo(UserTagName other) {
return this.getDisplayName().toLowerCase().compareTo(other.getDisplayName().toLowerCase());
}
@Override
public int hashCode() {
int hash = 7;
hash = 83 * hash + Objects.hashCode(this.displayName);
return hash;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof UserTagName)) {
return false;
}
UserTagName thatTagName = (UserTagName) obj;
return this.getDisplayName().equals(thatTagName.getDisplayName());
}
@Override
public String toString() {
return displayName;
}
/**
* @return A string representation of the tag name in the format that is
* used by the properties file.
*/
public String toSettingsFormat() {
return displayName + "," + description + "," + colorName;
}
}