diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 2c5cdb94bc269249001647d01aa8e50225684885..06658ee236441eaa9ec352bbe2bff1530dc1ea8f 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -11351,10 +11351,11 @@ public List<TagName> getTagNamesInUse(long dsObjId) throws TskCoreException { * @return A TagName data transfer object (DTO) for the new row. * * @throws TskCoreException - * @deprecated addOrUpdateTagName should be used this method calls - * addOrUpdateTagName with a default knownStatus value + * @deprecated TaggingManager.addOrUpdateTagName should be used instead + * with the default knowStatus of TskData.FileKnown.UNKNOWN */ @Deprecated + @SuppressWarnings("deprecation") public TagName addTagName(String displayName, String description, TagName.HTML_COLOR color) throws TskCoreException { return addOrUpdateTagName(displayName, description, color, TskData.FileKnown.UNKNOWN); } @@ -11372,35 +11373,11 @@ public TagName addTagName(String displayName, String description, TagName.HTML_C * @return A TagName data transfer object (DTO) for the new row. * * @throws TskCoreException + * @deprecated This method has been replaced by TaggingManager.addOrUpdateTagName. */ + @Deprecated public TagName addOrUpdateTagName(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown knownStatus) throws TskCoreException { - acquireSingleUserCaseWriteLock(); - try (CaseDbConnection connection = connections.getConnection();) { - PreparedStatement statement; - // INSERT INTO tag_names (display_name, description, color, knownStatus) VALUES (?, ?, ?, ?) ON CONFLICT (display_name) DO UPDATE SET description = ?, color = ?, knownStatus = ? - statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_OR_UPDATE_TAG_NAME, Statement.RETURN_GENERATED_KEYS); - statement.clearParameters(); - statement.setString(5, description); - statement.setString(6, color.getName()); - statement.setByte(7, knownStatus.getFileKnownValue()); - statement.setString(1, displayName); - statement.setString(2, description); - statement.setString(3, color.getName()); - statement.setByte(4, knownStatus.getFileKnownValue()); - connection.executeUpdate(statement); - - statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_TAG_NAME_BY_NAME); - statement.clearParameters(); - statement.setString(1, displayName); - try (ResultSet resultSet = connection.executeQuery(statement)) { - resultSet.next(); - return new TagName(resultSet.getLong("tag_name_id"), displayName, description, color, knownStatus, resultSet.getLong("tag_set_id"), resultSet.getInt("rank")); - } - } catch (SQLException ex) { - throw new TskCoreException("Error adding row for " + displayName + " tag name to tag_names table", ex); - } finally { - releaseSingleUserCaseWriteLock(); - } + return getTaggingManager().addOrUpdateTagName(displayName, description, color, knownStatus); } /** @@ -12922,7 +12899,6 @@ private enum PREPARED_STATEMENT { + "FROM tsk_objects INNER JOIN blackboard_artifacts " //NON-NLS + "ON tsk_objects.obj_id=blackboard_artifacts.obj_id " //NON-NLS + "WHERE (tsk_objects.par_obj_id = ?)"), - INSERT_OR_UPDATE_TAG_NAME("INSERT INTO tag_names (display_name, description, color, knownStatus) VALUES (?, ?, ?, ?) ON CONFLICT (display_name) DO UPDATE SET description = ?, color = ?, knownStatus = ?"), SELECT_EXAMINER_BY_ID("SELECT * FROM tsk_examiners WHERE examiner_id = ?"), SELECT_EXAMINER_BY_LOGIN_NAME("SELECT * FROM tsk_examiners WHERE login_name = ?"), INSERT_EXAMINER_POSTGRESQL("INSERT INTO tsk_examiners (login_name) VALUES (?) ON CONFLICT DO NOTHING"), @@ -12941,8 +12917,7 @@ private enum PREPARED_STATEMENT { INSERT_POOL_INFO("INSERT INTO tsk_pool_info (obj_id, pool_type) VALUES (?, ?)"), INSERT_FS_INFO("INSERT INTO tsk_fs_info (obj_id, data_source_obj_id, img_offset, fs_type, block_size, block_count, root_inum, first_inum, last_inum, display_name)" + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), - SELECT_TAG_NAME_BY_ID("SELECT * FROM tag_names where tag_name_id = ?"), - SELECT_TAG_NAME_BY_NAME("SELECT * FROM tag_names where display_name = ?"); + SELECT_TAG_NAME_BY_ID("SELECT * FROM tag_names where tag_name_id = ?"); private final String sql; diff --git a/bindings/java/src/org/sleuthkit/datamodel/TagSet.java b/bindings/java/src/org/sleuthkit/datamodel/TagSet.java index 31e86808e21e04a915bb1ecb40da58b7c175469b..8c8dec9ca15b8ffeee706db08587c42b34a8f462 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/TagSet.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TagSet.java @@ -72,7 +72,7 @@ public List<TagName> getTagNames() { * * @return TagSet id value. */ - long getId() { + public long getId() { return id; } diff --git a/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java b/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java index 52b8fcddf7cb4d9ff93886dfa69cc9e7d66c6528..f568b08fd09fb5e5d7d42f2f8d27f348ff00a1eb 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java @@ -1,7 +1,7 @@ /* * Sleuth Kit Data Model * - * Copyright 2020 Basis Technology Corp. + * Copyright 2020-2021 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,6 +18,7 @@ */ package org.sleuthkit.datamodel; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; @@ -28,6 +29,11 @@ import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction; import static org.sleuthkit.datamodel.TskData.DbType.POSTGRESQL; +import org.sleuthkit.datamodel.TskEvent.TagNamesAddedTskEvent; +import org.sleuthkit.datamodel.TskEvent.TagNamesDeletedTskEvent; +import org.sleuthkit.datamodel.TskEvent.TagNamesUpdatedTskEvent; +import org.sleuthkit.datamodel.TskEvent.TagSetsAddedTskEvent; +import org.sleuthkit.datamodel.TskEvent.TagSetsDeletedTskEvent; /** * Provides an API to manage Tags. @@ -54,10 +60,10 @@ public class TaggingManager { */ public List<TagSet> getTagSets() throws TskCoreException { List<TagSet> tagSetList = new ArrayList<>(); - + skCase.acquireSingleUserCaseReadLock(); String getAllTagSetsQuery = "SELECT * FROM tsk_tag_sets"; - try (CaseDbConnection connection = skCase.getConnection();Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(getAllTagSetsQuery);) { + try (CaseDbConnection connection = skCase.getConnection(); Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(getAllTagSetsQuery);) { while (resultSet.next()) { int setID = resultSet.getInt("tag_set_id"); String setName = resultSet.getString("name"); @@ -121,6 +127,8 @@ public TagSet addTagSet(String name, List<TagName> tagNames) throws TskCoreExcep } } tagSet = new TagSet(setID, name, updatedTags); + skCase.fireTSKEvent(new TagSetsAddedTskEvent(Collections.singletonList(tagSet))); + skCase.fireTSKEvent(new TagNamesUpdatedTskEvent(updatedTags)); } trans.commit(); } catch (SQLException ex) { @@ -158,6 +166,14 @@ public void deleteTagSet(TagSet tagSet) throws TskCoreException { queryTemplate = "DELETE FROM tsk_tag_sets WHERE tag_set_id = '%d'"; stmt.execute(String.format(queryTemplate, tagSet.getId())); trans.commit(); + + List<Long> tagNameIds = new ArrayList<>(); + for (TagName tagName : tagSet.getTagNames()) { + tagNameIds.add(tagName.getId()); + } + + skCase.fireTSKEvent(new TagSetsDeletedTskEvent(Collections.singletonList(tagSet.getId()))); + skCase.fireTSKEvent(new TagNamesDeletedTskEvent(tagNameIds)); } catch (SQLException ex) { trans.rollback(); throw new TskCoreException(String.format("Error deleting tag set where id = %d.", tagSet.getId()), ex); @@ -177,15 +193,15 @@ public TagSet getTagSet(TagName tagName) throws TskCoreException { if (tagName == null) { throw new IllegalArgumentException("Null tagName argument"); } - + if (tagName.getTagSetId() <= 0) { return null; } - + skCase.acquireSingleUserCaseReadLock(); TagSet tagSet = null; String sqlQuery = String.format("SELECT * FROM tsk_tag_sets WHERE tag_set_id = %d", tagName.getTagSetId()); - try (CaseDbConnection connection = skCase.getConnection();Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(sqlQuery);) { + try (CaseDbConnection connection = skCase.getConnection(); Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(sqlQuery);) { if (resultSet.next()) { int setID = resultSet.getInt("tag_set_id"); String setName = resultSet.getString("name"); @@ -199,6 +215,39 @@ public TagSet getTagSet(TagName tagName) throws TskCoreException { } } + /** + * Return a TagSet object for the given id. + * + * @param id TagSet id. + * + * @return The TagSet represented by the given it, or null if one was not + * found. + * + * @throws TskCoreException + */ + public TagSet getTagSet(long id) throws TskCoreException { + TagSet tagSet = null; + String preparedQuery = "Select * FROM tsk_tag_sets WHERE tag_set_id = ?"; + skCase.acquireSingleUserCaseReadLock(); + try (CaseDbConnection connection = skCase.getConnection(); PreparedStatement statement = connection.getPreparedStatement(preparedQuery, Statement.NO_GENERATED_KEYS)) { + statement.setLong(1, id); + try (ResultSet resultSet = statement.executeQuery()) { + if (resultSet.next()) { + int setID = resultSet.getInt("tag_set_id"); + String setName = resultSet.getString("name"); + tagSet = new TagSet(setID, setName, getTagNamesByTagSetID(setID)); + } + } + + } catch (SQLException ex) { + throw new TskCoreException(String.format("Error occurred getting TagSet (ID=%d)", id), ex); + } finally { + skCase.releaseSingleUserCaseReadLock(); + } + + return tagSet; + } + /** * Inserts a row into the blackboard_artifact_tags table in the case * database. @@ -216,7 +265,7 @@ public BlackboardArtifactTagChange addArtifactTag(BlackboardArtifact artifact, T if (artifact == null || tagName == null) { throw new IllegalArgumentException("NULL argument passed to addArtifactTag"); } - + List<BlackboardArtifactTag> removedTags = new ArrayList<>(); List<String> removedTagIds = new ArrayList<>(); CaseDbTransaction trans = null; @@ -254,15 +303,14 @@ public BlackboardArtifactTagChange addArtifactTag(BlackboardArtifact artifact, T } } - } - + Content content = skCase.getContentById(artifact.getObjectID()); Examiner currentExaminer = skCase.getCurrentExaminer(); - + trans = skCase.beginTransaction(); CaseDbConnection connection = trans.getConnection(); - + if (!removedTags.isEmpty()) { // Remove the tags. String removeQuery = String.format("DELETE FROM blackboard_artifact_tags WHERE tag_id IN (%s)", String.join(",", removedTagIds)); @@ -274,7 +322,7 @@ public BlackboardArtifactTagChange addArtifactTag(BlackboardArtifact artifact, T // Add the new Tag. BlackboardArtifactTag artifactTag; try (Statement stmt = connection.createStatement()) { - + String query = String.format( "INSERT INTO blackboard_artifact_tags (artifact_id, tag_name_id, comment, examiner_id) VALUES (%d, %d, '%s', %d)", artifact.getArtifactID(), @@ -294,7 +342,7 @@ public BlackboardArtifactTagChange addArtifactTag(BlackboardArtifact artifact, T artifact, content, tagName, comment, currentExaminer.getLoginName()); } } - + skCase.getScoringManager().updateAggregateScoreAfterAddition( artifact.getId(), artifact.getDataSourceObjectID(), getTagScore(tagName.getKnownStatus()), trans); @@ -302,35 +350,36 @@ public BlackboardArtifactTagChange addArtifactTag(BlackboardArtifact artifact, T return new BlackboardArtifactTagChange(artifactTag, removedTags); } catch (SQLException ex) { - if(trans != null) { + if (trans != null) { trans.rollback(); } throw new TskCoreException("Error adding row to blackboard_artifact_tags table (obj_id = " + artifact.getArtifactID() + ", tag_name_id = " + tagName.getId() + ")", ex); } } - /** * Returns the score based on this TagName object. + * * @param knownStatus The known status of the tag. + * * @return The relevant score. */ static Score getTagScore(TskData.FileKnown knownStatus) { switch (knownStatus) { - case BAD: + case BAD: return Score.SCORE_NOTABLE; - case UNKNOWN: + case UNKNOWN: case KNOWN: default: return Score.SCORE_LIKELY_NOTABLE; } } - - /** + + /** * Retrieves the maximum FileKnown status of any tag associated with the * object id. * - * @param objectId The object id of the item. + * @param objectId The object id of the item. * @param transaction The case db transaction to perform this query. * * @return The maximum FileKnown status for this object or empty. @@ -385,7 +434,7 @@ public ContentTagChange addContentTag(Content content, TagName tagName, String c Examiner currentExaminer = skCase.getCurrentExaminer(); CaseDbTransaction trans = skCase.beginTransaction(); CaseDbConnection connection = trans.getConnection(); - + try { long tagSetId = tagName.getTagSetId(); @@ -429,7 +478,7 @@ public ContentTagChange addContentTag(Content content, TagName tagName, String c String queryTemplate = "INSERT INTO content_tags (obj_id, tag_name_id, comment, begin_byte_offset, end_byte_offset, examiner_id) VALUES (%d, %d, '%s', %d, %d, %d)"; ContentTag contentTag = null; try (Statement stmt = connection.createStatement()) { - + String query = String.format(queryTemplate, content.getId(), tagName.getId(), @@ -450,7 +499,7 @@ public ContentTagChange addContentTag(Content content, TagName tagName, String c content, tagName, comment, beginByteOffset, endByteOffset, currentExaminer.getLoginName()); } } - + Long dataSourceId = content.getDataSource() != null ? content.getDataSource().getId() : null; skCase.getScoringManager().updateAggregateScoreAfterAddition( content.getId(), dataSourceId, getTagScore(tagName.getKnownStatus()), trans); @@ -463,6 +512,104 @@ public ContentTagChange addContentTag(Content content, TagName tagName, String c } } + /** + * Inserts row into the tags_names table, or updates the existing row if the + * displayName already exists in the tag_names table in the case database. + * + * @param displayName The display name for the new tag name. + * @param description The description for the new tag name. + * @param color The HTML color to associate with the new tag name. + * @param knownStatus The TskData.FileKnown value to associate with the new + * tag name. + * + * @return A TagName data transfer object (DTO) for the new row. + * + * @throws TskCoreException + */ + public TagName addOrUpdateTagName(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown knownStatus) throws TskCoreException { + String insertQuery = "INSERT INTO tag_names (display_name, description, color, knownStatus) VALUES (?, ?, ?, ?) ON CONFLICT (display_name) DO UPDATE SET description = ?, color = ?, knownStatus = ?"; + boolean isUpdated = false; + skCase.acquireSingleUserCaseWriteLock(); + try (CaseDbConnection connection = skCase.getConnection()) { + try (PreparedStatement statement = connection.getPreparedStatement("SELECT * FROM tag_names WHERE display_name = ?", Statement.NO_GENERATED_KEYS)) { + statement.setString(1, displayName); + try (ResultSet resultSet = statement.executeQuery()) { + isUpdated = resultSet.next(); + } + } + + try (PreparedStatement statement = connection.getPreparedStatement(insertQuery, Statement.RETURN_GENERATED_KEYS);) { + statement.clearParameters(); + statement.setString(5, description); + statement.setString(6, color.getName()); + statement.setByte(7, knownStatus.getFileKnownValue()); + statement.setString(1, displayName); + statement.setString(2, description); + statement.setString(3, color.getName()); + statement.setByte(4, knownStatus.getFileKnownValue()); + statement.executeUpdate(); + } + + try (PreparedStatement statement = connection.getPreparedStatement("SELECT * FROM tag_names where display_name = ?", Statement.NO_GENERATED_KEYS)) { + statement.setString(1, displayName); + try (ResultSet resultSet = connection.executeQuery(statement)) { + resultSet.next(); + TagName newTag = new TagName(resultSet.getLong("tag_name_id"), displayName, description, color, knownStatus, resultSet.getLong("tag_set_id"), resultSet.getInt("rank")); + + if (!isUpdated) { + skCase.fireTSKEvent(new TagNamesAddedTskEvent(Collections.singletonList(newTag))); + } else { + skCase.fireTSKEvent(new TagNamesUpdatedTskEvent(Collections.singletonList(newTag))); + } + + return newTag; + } + } + } catch (SQLException ex) { + throw new TskCoreException("Error adding row for " + displayName + " tag name to tag_names table", ex); + } finally { + skCase.releaseSingleUserCaseWriteLock(); + } + } + + /** + * Return the TagName object for the given id. + * + * @param id The TagName id. + * + * @return The TagName object for the given id. + * + * @throws TskCoreException + */ + public TagName getTagName(long id) throws TskCoreException { + String preparedQuery = "SELECT * FROM tag_names where tag_name_id = ?"; + + skCase.acquireSingleUserCaseReadLock(); + try (CaseDbConnection connection = skCase.getConnection()) { + try (PreparedStatement statement = connection.getPreparedStatement(preparedQuery, Statement.NO_GENERATED_KEYS)) { + statement.clearParameters(); + statement.setLong(1, id); + try (ResultSet resultSet = statement.executeQuery()) { + if (resultSet.next()) { + return new TagName(resultSet.getLong("tag_name_id"), + resultSet.getString("display_name"), + resultSet.getString("description"), + TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), + TskData.FileKnown.valueOf(resultSet.getByte("knowStatus")), + resultSet.getLong("tag_set_id"), + resultSet.getInt("rank")); + } + } + } + } catch (SQLException ex) { + throw new TskCoreException("", ex); + } finally { + skCase.releaseSingleUserCaseWriteLock(); + } + + return null; + } + /** * Determine if the given TagSet contains TagNames that are currently in * use, ie there is an existing ContentTag or ArtifactTag that uses TagName. @@ -522,7 +669,7 @@ private List<TagName> getTagNamesByTagSetID(int tagSetId) throws TskCoreExceptio skCase.acquireSingleUserCaseReadLock(); String query = String.format("SELECT * FROM tag_names WHERE tag_set_id = %d", tagSetId); - try (CaseDbConnection connection = skCase.getConnection();Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(query)) { + try (CaseDbConnection connection = skCase.getConnection(); Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(query)) { while (resultSet.next()) { tagNameList.add(new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), diff --git a/bindings/java/src/org/sleuthkit/datamodel/TskEvent.java b/bindings/java/src/org/sleuthkit/datamodel/TskEvent.java index d67686c466ace311eec1d2da324fbdd517d4929f..f02623b931fd5b9aa9ff3c7311a85b4657b683c0 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/TskEvent.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TskEvent.java @@ -462,7 +462,7 @@ public final static class HostsRemovedFromPersonTskEvent extends TskObjectsEvent private final Person person; /** - * Contructs an event published when one or more hosts are removed from + * Constructs an event published when one or more hosts are removed from * a person. * * @param person The person. @@ -493,4 +493,123 @@ public List<Long> getHostIds() { } + static abstract class TagNamesTskEvent extends TskObjectsEvent<TagName> { + + public TagNamesTskEvent(List<TagName> tagNames) { + super(tagNames); + } + + /** + * Returns the list of added or updated TagName objects. + * + * @return The TagName list. + */ + public List<TagName> getTagNames() { + return getDataModelObjects(); + } + + } + + /** + * An event published when one or more TagName are added. + */ + public final static class TagNamesAddedTskEvent extends TagNamesTskEvent { + + /** + * Construct an event when one or more TagName are created or updated. + * + * @param tagNames List of added or modified TagName. + */ + public TagNamesAddedTskEvent(List<TagName> tagNames) { + super(tagNames); + } + } + + /** + * An event published when one or more TagName are updated. + */ + public final static class TagNamesUpdatedTskEvent extends TagNamesTskEvent { + + /** + * Construct an event when one or more TagName are updated. + * + * @param tagNames List of added or modified TagName. + */ + public TagNamesUpdatedTskEvent(List<TagName> tagNames) { + super(tagNames); + } + } + + /** + * An event published when one or more TagName are deleted. + */ + public final static class TagNamesDeletedTskEvent extends TskObjectsEvent<Long> { + + /** + * Constructs a new event with the given list of TagName ids. + * + * @param tagNameIds Deleted TagName id list. + */ + public TagNamesDeletedTskEvent(List<Long> tagNameIds) { + super(tagNameIds); + } + + /** + * List of the deleted TagName ids. + * + * @return The list of deleted TagName Ids. + */ + public List<Long> getTagNameIds() { + return getDataModelObjects(); + } + + } + + /** + * An event published when one or more TagSets have been added. + */ + public final static class TagSetsAddedTskEvent extends TskObjectsEvent<TagSet> { + + /** + * Constructs an added event for one or more TagSets. + * + * @param tagSets The added TagSet. + */ + public TagSetsAddedTskEvent(List<TagSet> tagSets) { + super(tagSets); + } + + /** + * Return the TagSets list. + * + * @return The TagSet list. + */ + public List<TagSet> getTagSets() { + return getDataModelObjects(); + } + } + + /** + * An event published when one or more TagSets have been deleted. + */ + public final static class TagSetsDeletedTskEvent extends TskObjectsEvent<Long> { + + /** + * Constructs a deleted event for one or more TagSets. + * + * @param tagSetIds The ids of the deleted TagSets. + */ + public TagSetsDeletedTskEvent(List<Long> tagSetIds) { + super(tagSetIds); + } + + /** + * Returns the list of deleted TagSet ids. + * + * @return The list of deleted TagSet ids. + */ + public List<Long> getTagSetIds() { + return getDataModelObjects(); + } + } }