diff --git a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java index ecd94704b74cdf989590cee7be892fc220c45ad5..0de142d73c1fe09aadcd10400dd84fb54fc23045 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java +++ b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java @@ -260,8 +260,10 @@ private void createArtifactTables(Statement stmt) throws SQLException { } private void createTagTables(Statement stmt) throws SQLException { + stmt.execute("CREATE TABLE tsk_tag_sets (tag_set_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, name TEXT UNIQUE)"); stmt.execute("CREATE TABLE tag_names (tag_name_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, display_name TEXT UNIQUE, " - + "description TEXT NOT NULL, color TEXT NOT NULL, knownStatus INTEGER NOT NULL)"); + + "description TEXT NOT NULL, color TEXT NOT NULL, knownStatus INTEGER NOT NULL," + + " tag_set_id INTEGER, FOREIGN KEY(tag_set_id) REFERENCES tsk_tag_sets(tag_set_id) ON DELETE SET NULL)"); stmt.execute("CREATE TABLE tsk_examiners (examiner_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, " + "login_name TEXT NOT NULL, display_name TEXT, UNIQUE(login_name))"); diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 610046702d3cbaa0f2d740ae266b288d95ddd0e2..bd102bf42a5a61accdeeddf4d2efb054352778c5 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -97,7 +97,7 @@ public class SleuthkitCase { * tsk/auto/tsk_db.h. */ static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION - = new CaseDbSchemaVersionNumber(8, 4); + = new CaseDbSchemaVersionNumber(8, 5); private static final long BASE_ARTIFACT_ID = Long.MIN_VALUE; // Artifact ids will start at the lowest negative value private static final Logger logger = Logger.getLogger(SleuthkitCase.class.getName()); @@ -207,6 +207,7 @@ public class SleuthkitCase { private TimelineManager timelineMgr; private Blackboard blackboard; private CaseDbAccessManager dbAccessManager; + private TaggingManager taggingMgr; private final Map<String, Set<Long>> deviceIdToDatasourceObjIdMap = new HashMap<>(); @@ -375,6 +376,7 @@ private void init() throws Exception { communicationsMgr = new CommunicationsManager(this); timelineMgr = new TimelineManager(this); dbAccessManager = new CaseDbAccessManager(this); + taggingMgr = new TaggingManager(this); } /** @@ -477,6 +479,15 @@ public TimelineManager getTimelineManager() throws TskCoreException { public synchronized CaseDbAccessManager getCaseDbAccessManager() throws TskCoreException { return dbAccessManager; } + + /** + * Get the case database TaggingManager object. + * + * @return The per case TaggingManager object. + */ + public synchronized TaggingManager getTaggingManager() { + return taggingMgr; + } /** * Make sure the predefined artifact types are in the artifact types table. @@ -892,6 +903,7 @@ private void updateDatabaseSchema(String dbPath) throws Exception { dbSchemaVersion = updateFromSchema8dot1toSchema8dot2(dbSchemaVersion, connection); dbSchemaVersion = updateFromSchema8dot2toSchema8dot3(dbSchemaVersion, connection); dbSchemaVersion = updateFromSchema8dot3toSchema8dot4(dbSchemaVersion, connection); + dbSchemaVersion = updateFromSchema8dot4toSchema8dot5(dbSchemaVersion, connection); statement = connection.createStatement(); connection.executeUpdate(statement, "UPDATE tsk_db_info SET schema_ver = " + dbSchemaVersion.getMajor() + ", schema_minor_ver = " + dbSchemaVersion.getMinor()); //NON-NLS connection.executeUpdate(statement, "UPDATE tsk_db_info_extended SET value = " + dbSchemaVersion.getMajor() + " WHERE name = '" + SCHEMA_MAJOR_VERSION_KEY + "'"); //NON-NLS @@ -2084,6 +2096,58 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot3toSchema8dot4(CaseDbSchem releaseSingleUserCaseWriteLock(); } } + + private CaseDbSchemaVersionNumber updateFromSchema8dot4toSchema8dot5(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { + if (schemaVersion.getMajor() != 8) { + return schemaVersion; + } + + if (schemaVersion.getMinor() != 4) { + return schemaVersion; + } + + Statement statement = connection.createStatement(); + acquireSingleUserCaseWriteLock(); + try { + switch (getDatabaseType()) { + case POSTGRESQL: + statement.execute("CREATE TABLE tsk_tag_sets (tag_set_id BIGSERIAL PRIMARY KEY, name TEXT UNIQUE)"); + break; + case SQLITE: + statement.execute("CREATE TABLE tsk_tag_sets (tag_set_id INTEGER PRIMARY KEY, name TEXT UNIQUE)"); + break; + } + + statement.execute("ALTER TABLE tag_names ADD COLUMN tag_set_id INTEGER REFERENCES tsk_tag_sets(tag_set_id)"); + + String insertStmt = "INSERT INTO tsk_tag_sets (name) VALUES ('Project VIC (United States)')"; + if (getDatabaseType() == DbType.POSTGRESQL) { + statement.execute(insertStmt, Statement.RETURN_GENERATED_KEYS); + } else { + statement.execute(insertStmt); + } + try (ResultSet resultSet = statement.getGeneratedKeys()) { + if (resultSet != null && resultSet.next()) { + int tagSetId = resultSet.getInt(1); + + String updateQuery = "UPDATE tag_names SET tag_set_id = %d, color = '%s' WHERE display_name = '%s'"; + statement.executeUpdate(String.format(updateQuery, tagSetId, "Red", "CAT-1: Child Exploitation (Illegal)")); + statement.executeUpdate(String.format(updateQuery, tagSetId, "Lime", "CAT-2: Child Exploitation (Non-Illegal/Age Difficult)")); + statement.executeUpdate(String.format(updateQuery, tagSetId, "Yellow", "CAT-3: CGI/Animation (Child Exploitive)")); + statement.executeUpdate(String.format(updateQuery, tagSetId, "Purple", "CAT-4: Exemplar/Comparison (Internal Use Only)")); + statement.executeUpdate(String.format(updateQuery, tagSetId, "Green", "CAT-5: Non-pertinent")); + statement.executeUpdate(String.format(updateQuery, tagSetId, "Silver", "CAT-0: Uncategorized")); + } else { + throw new TskCoreException("Failed to retrieve the default tag_set_id from DB"); + } + } + return new CaseDbSchemaVersionNumber(8, 5); + + } finally { + closeStatement(statement); + releaseSingleUserCaseWriteLock(); + } + } /** * Inserts a row for the given account type in account_types table, @@ -9549,11 +9613,11 @@ public List<TagName> getAllTagNames() throws TskCoreException { // SELECT * FROM tag_names PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_TAG_NAMES); resultSet = connection.executeQuery(statement); - ArrayList<TagName> tagNames = new ArrayList<TagName>(); + ArrayList<TagName> tagNames = new ArrayList<>(); while (resultSet.next()) { tagNames.add(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("knownStatus")))); //NON-NLS + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getInt("tag_set_id"))); //NON-NLS } return tagNames; } catch (SQLException ex) { @@ -9583,11 +9647,11 @@ public List<TagName> getTagNamesInUse() throws TskCoreException { // SELECT * FROM tag_names WHERE tag_name_id IN (SELECT tag_name_id from content_tags UNION SELECT tag_name_id FROM blackboard_artifact_tags) PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_TAG_NAMES_IN_USE); resultSet = connection.executeQuery(statement); - ArrayList<TagName> tagNames = new ArrayList<TagName>(); + ArrayList<TagName> tagNames = new ArrayList<>(); while (resultSet.next()) { tagNames.add(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("knownStatus")))); //NON-NLS + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id"))); //NON-NLS } return tagNames; } catch (SQLException ex) { @@ -9631,7 +9695,7 @@ public List<TagName> getTagNamesInUse(long dsObjId) throws TskCoreException { while (resultSet.next()) { tagNames.add(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("knownStatus")))); //NON-NLS + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id"))); //NON-NLS } return tagNames; } catch (SQLException ex) { @@ -9695,7 +9759,7 @@ public TagName addOrUpdateTagName(String displayName, String description, TagNam resultSet = statement.getGeneratedKeys(); resultSet.next(); return new TagName(resultSet.getLong(1), //last_insert_rowid() - displayName, description, color, knownStatus); + displayName, description, color, knownStatus, 0); } catch (SQLException ex) { throw new TskCoreException("Error adding row for " + displayName + " tag name to tag_names table", ex); } finally { @@ -9717,34 +9781,11 @@ public TagName addOrUpdateTagName(String displayName, String description, TagNam * @return A ContentTag data transfer object (DTO) for the new row. * * @throws TskCoreException + * @deprecated Use TaggingManager.addContentTag */ + @Deprecated public ContentTag addContentTag(Content content, TagName tagName, String comment, long beginByteOffset, long endByteOffset) throws TskCoreException { - CaseDbConnection connection = connections.getConnection(); - acquireSingleUserCaseWriteLock(); - ResultSet resultSet = null; - try { - Examiner currentExaminer = getCurrentExaminer(); - // INSERT INTO content_tags (obj_id, tag_name_id, comment, begin_byte_offset, end_byte_offset, examiner_id) VALUES (?, ?, ?, ?, ?, ?) - PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_CONTENT_TAG, Statement.RETURN_GENERATED_KEYS); - statement.clearParameters(); - statement.setLong(1, content.getId()); - statement.setLong(2, tagName.getId()); - statement.setString(3, comment); - statement.setLong(4, beginByteOffset); - statement.setLong(5, endByteOffset); - statement.setLong(6, currentExaminer.getId()); - connection.executeUpdate(statement); - resultSet = statement.getGeneratedKeys(); - resultSet.next(); - return new ContentTag(resultSet.getLong(1), //last_insert_rowid() - content, tagName, comment, beginByteOffset, endByteOffset, currentExaminer.getLoginName()); - } catch (SQLException ex) { - throw new TskCoreException("Error adding row to content_tags table (obj_id = " + content.getId() + ", tag_name_id = " + tagName.getId() + ")", ex); - } finally { - closeResultSet(resultSet); - connection.close(); - releaseSingleUserCaseWriteLock(); - } + return taggingMgr.addContentTag(content, tagName, comment, beginByteOffset, endByteOffset).getAddedTag(); } /* @@ -9792,7 +9833,7 @@ public List<ContentTag> getAllContentTags() throws TskCoreException { while (resultSet.next()) { TagName tagName = 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("knownStatus"))); //NON-NLS + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id")); //NON-NLS Content content = getContentById(resultSet.getLong("obj_id")); //NON-NLS tags.add(new ContentTag(resultSet.getLong("tag_id"), content, tagName, resultSet.getString("comment"), resultSet.getLong("begin_byte_offset"), resultSet.getLong("end_byte_offset"), resultSet.getString("login_name"))); //NON-NLS @@ -9922,7 +9963,7 @@ public ContentTag getContentTagByID(long contentTagID) throws TskCoreException { while (resultSet.next()) { TagName tagName = 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("knownStatus"))); + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id")); tag = new ContentTag(resultSet.getLong("tag_id"), getContentById(resultSet.getLong("obj_id")), tagName, resultSet.getString("comment"), resultSet.getLong("begin_byte_offset"), resultSet.getLong("end_byte_offset"), resultSet.getString("login_name")); } @@ -10058,7 +10099,7 @@ public List<ContentTag> getContentTagsByContent(Content content) throws TskCoreE while (resultSet.next()) { TagName tagName = 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("knownStatus"))); //NON-NLS + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id")); //NON-NLS ContentTag tag = new ContentTag(resultSet.getLong("tag_id"), content, tagName, resultSet.getString("comment"), resultSet.getLong("begin_byte_offset"), resultSet.getLong("end_byte_offset"), resultSet.getString("login_name")); //NON-NLS tags.add(tag); @@ -10085,32 +10126,11 @@ public List<ContentTag> getContentTagsByContent(Content content) throws TskCoreE * row. * * @throws TskCoreException + * @Deprecated User TaggingManager.addArtifactTag instead. */ + @Deprecated public BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName, String comment) throws TskCoreException { - CaseDbConnection connection = connections.getConnection(); - acquireSingleUserCaseWriteLock(); - ResultSet resultSet = null; - try { - Examiner currentExaminer = getCurrentExaminer(); - // "INSERT INTO blackboard_artifact_tags (artifact_id, tag_name_id, comment, examiner_id) VALUES (?, ?, ?, ?)"), //NON-NLS - PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_ARTIFACT_TAG, Statement.RETURN_GENERATED_KEYS); - statement.clearParameters(); - statement.setLong(1, artifact.getArtifactID()); - statement.setLong(2, tagName.getId()); - statement.setString(3, comment); - statement.setLong(4, currentExaminer.getId()); - connection.executeUpdate(statement); - resultSet = statement.getGeneratedKeys(); - resultSet.next(); - return new BlackboardArtifactTag(resultSet.getLong(1), //last_insert_rowid() - artifact, getContentById(artifact.getObjectID()), tagName, comment, currentExaminer.getLoginName()); - } catch (SQLException ex) { - throw new TskCoreException("Error adding row to blackboard_artifact_tags table (obj_id = " + artifact.getArtifactID() + ", tag_name_id = " + tagName.getId() + ")", ex); - } finally { - closeResultSet(resultSet); - connection.close(); - releaseSingleUserCaseWriteLock(); - } + return taggingMgr.addArtifactTag(artifact, tagName, comment).getAddedTag(); } /* @@ -10155,11 +10175,11 @@ public List<BlackboardArtifactTag> getAllBlackboardArtifactTags() throws TskCore // LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ARTIFACT_TAGS); resultSet = connection.executeQuery(statement); - ArrayList<BlackboardArtifactTag> tags = new ArrayList<BlackboardArtifactTag>(); + ArrayList<BlackboardArtifactTag> tags = new ArrayList<>(); while (resultSet.next()) { TagName tagName = 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("knownStatus"))); //NON-NLS + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id")); //NON-NLS BlackboardArtifact artifact = getBlackboardArtifact(resultSet.getLong("artifact_id")); //NON-NLS Content content = getContentById(artifact.getObjectID()); BlackboardArtifactTag tag = new BlackboardArtifactTag(resultSet.getLong("tag_id"), @@ -10389,7 +10409,7 @@ public BlackboardArtifactTag getBlackboardArtifactTagByID(long artifactTagID) th while (resultSet.next()) { TagName tagName = 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("knownStatus"))); + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id")); BlackboardArtifact artifact = getBlackboardArtifact(resultSet.getLong("artifact_id")); //NON-NLS Content content = getContentById(artifact.getObjectID()); tag = new BlackboardArtifactTag(resultSet.getLong("tag_id"), @@ -10433,11 +10453,11 @@ public List<BlackboardArtifactTag> getBlackboardArtifactTagsByArtifact(Blackboar statement.clearParameters(); statement.setLong(1, artifact.getArtifactID()); resultSet = connection.executeQuery(statement); - ArrayList<BlackboardArtifactTag> tags = new ArrayList<BlackboardArtifactTag>(); + ArrayList<BlackboardArtifactTag> tags = new ArrayList<>(); while (resultSet.next()) { TagName tagName = 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("knownStatus"))); //NON-NLS + TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")), resultSet.getLong("tag_set_id")); //NON-NLS Content content = getContentById(artifact.getObjectID()); BlackboardArtifactTag tag = new BlackboardArtifactTag(resultSet.getLong("tag_id"), artifact, content, tagName, resultSet.getString("comment"), resultSet.getString("login_name")); //NON-NLS @@ -11543,7 +11563,7 @@ private enum PREPARED_STATEMENT { + " AND content_tags.tag_name_id = ? " + " AND tsk_files.data_source_obj_id = ? " ), - SELECT_CONTENT_TAGS("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name " + SELECT_CONTENT_TAGS("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id " + "FROM content_tags " + "INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id " + "LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id"), //NON-NLS @@ -11551,19 +11571,19 @@ private enum PREPARED_STATEMENT { + "FROM content_tags " + "LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id " + "WHERE tag_name_id = ?"), //NON-NLS - SELECT_CONTENT_TAGS_BY_TAG_NAME_BY_DATASOURCE("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name " + SELECT_CONTENT_TAGS_BY_TAG_NAME_BY_DATASOURCE("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id " + "FROM content_tags as content_tags, tsk_files as tsk_files, tag_names as tag_names, tsk_examiners as tsk_examiners " + "WHERE content_tags.examiner_id = tsk_examiners.examiner_id" + " AND content_tags.obj_id = tsk_files.obj_id" + " AND content_tags.tag_name_id = tag_names.tag_name_id" + " AND content_tags.tag_name_id = ?" + " AND tsk_files.data_source_obj_id = ? "), - SELECT_CONTENT_TAG_BY_ID("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name " + SELECT_CONTENT_TAG_BY_ID("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id " + "FROM content_tags " + "INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id " + "LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id " + "WHERE tag_id = ?"), //NON-NLS - SELECT_CONTENT_TAGS_BY_CONTENT("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name " + SELECT_CONTENT_TAGS_BY_CONTENT("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id " + "FROM content_tags " + "INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id " + "LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id " @@ -11589,12 +11609,12 @@ private enum PREPARED_STATEMENT { + " AND artifact_tags.artifact_id = arts.artifact_id" + " AND artifact_tags.tag_name_id = ? " + " AND arts.data_source_obj_id = ? "), - SELECT_ARTIFACT_TAG_BY_ID("SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name " + SELECT_ARTIFACT_TAG_BY_ID("SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id " + "FROM blackboard_artifact_tags " + "INNER JOIN tag_names ON blackboard_artifact_tags.tag_name_id = tag_names.tag_name_id " + "LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id " + "WHERE blackboard_artifact_tags.tag_id = ?"), //NON-NLS - SELECT_ARTIFACT_TAGS_BY_ARTIFACT("SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name " + SELECT_ARTIFACT_TAGS_BY_ARTIFACT("SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name, tag_names.tag_set_id " + "FROM blackboard_artifact_tags " + "INNER JOIN tag_names ON blackboard_artifact_tags.tag_name_id = tag_names.tag_name_id " + "LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id " diff --git a/bindings/java/src/org/sleuthkit/datamodel/TagName.java b/bindings/java/src/org/sleuthkit/datamodel/TagName.java index fdc729568ed53bf0e48f8de21ba284832daef20d..e404faeeb36d399690354c181b73467332b6b9df 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/TagName.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TagName.java @@ -80,14 +80,16 @@ public static HTML_COLOR getColorByName(String colorName) { private final String description; private final HTML_COLOR color; private final TskData.FileKnown knownStatus; - + private final long tagSetId; + // Clients of the org.sleuthkit.datamodel package should not directly create these objects. - TagName(long id, String displayName, String description, HTML_COLOR color, TskData.FileKnown knownStatus) { + TagName(long id, String displayName, String description, HTML_COLOR color, TskData.FileKnown knownStatus, long tagSetId) { this.id = id; this.displayName = displayName; this.description = description; this.color = color; this.knownStatus = knownStatus; + this.tagSetId = tagSetId; } public long getId() { @@ -110,6 +112,10 @@ public TskData.FileKnown getKnownStatus() { return knownStatus; } + long getTagSetId() { + return tagSetId; + } + /** * Compares two TagName objects by comparing their display names. * @@ -130,6 +136,7 @@ public int hashCode() { hash = 89 * hash + (this.description != null ? this.description.hashCode() : 0); hash = 89 * hash + (this.color != null ? this.color.hashCode() : 0); hash = 89 * hash + (this.knownStatus != null ? this.knownStatus.hashCode() : 0); + hash = 89 * hash + (int) (this.id ^ (this.tagSetId >>> 32)); return hash; } @@ -146,6 +153,7 @@ public boolean equals(Object obj) { && Objects.equals(this.displayName, other.displayName) && Objects.equals(this.description, other.description) && Objects.equals(this.color, other.color) - && Objects.equals(this.knownStatus, other.knownStatus)); + && Objects.equals(this.knownStatus, other.knownStatus) + && this.tagSetId == other.tagSetId); } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/TagSet.java b/bindings/java/src/org/sleuthkit/datamodel/TagSet.java new file mode 100755 index 0000000000000000000000000000000000000000..2821aff4d08f8534f93babfa886f1786b114b908 --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/TagSet.java @@ -0,0 +1,103 @@ +/* + * Sleuth Kit Data Model + * + * Copyright 2020 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.datamodel; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * A TagSet is a named group of TagNames. + */ +public class TagSet { + + private final String setName; + private final long id; + private final List<TagName> tagNameList; + + /** + * Construct a TagSet. + * + * @param id Tag set id value. + * @param setName Name of tag set. + */ + TagSet(long id, String setName, List<TagName> tagNameList) { + if (setName == null || setName.isEmpty()) { + throw new IllegalArgumentException("TagSet name must be a non-empty string"); + } + this.tagNameList = tagNameList; + this.id = id; + this.setName = setName; + } + + /** + * Returns the name of the tag set. + * + * @return Tag set name. + */ + public String getName() { + return setName; + } + + /** + * Returns a list of the TagName objects that belong to the tag set. + * + * @return An unmodifiable list of TagName objects. + */ + public List<TagName> getTagNames() { + return Collections.unmodifiableList(tagNameList); + } + + /** + * Return the TagSet id. + * + * @return TagSet id value. + */ + long getId() { + return id; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (getClass() != obj.getClass()) { + return false; + } + + final TagSet other = (TagSet) obj; + + return (this.id == other.getId() + && setName.equals(other.getName()) + && tagNameList.equals(other.tagNameList)); + } + + @Override + public int hashCode() { + int hash = 5; + hash = 89 * hash + (int) (this.id ^ (this.id >>> 32)); + hash = 89 * hash + Objects.hashCode(this.setName); + hash = 89 * hash + Objects.hashCode(this.tagNameList); + + return hash; + } + +} diff --git a/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java b/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java new file mode 100755 index 0000000000000000000000000000000000000000..6ff241ebee6b196484eee66f887e91f16a7b0da0 --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java @@ -0,0 +1,515 @@ +/* + * Sleuth Kit Data Model + * + * Copyright 2020 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.datamodel; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection; +import static org.sleuthkit.datamodel.TskData.DbType.POSTGRESQL; + +/** + * Provides an API to manage Tags. + */ +public class TaggingManager { + + private final SleuthkitCase skCase; + + /** + * Construct a TaggingManager for the given SleuthkitCase. + * + * @param skCase The SleuthkitCase. + */ + TaggingManager(SleuthkitCase skCase) { + this.skCase = skCase; + } + + /** + * Returns a list of all the TagSets that exist in the case. + * + * @return A List of TagSet objects or an empty list if none were found. + * + * @throws TskCoreException + */ + public List<TagSet> getTagSets() throws TskCoreException { + List<TagSet> tagSetList = new ArrayList<>(); + CaseDbConnection connection = skCase.getConnection(); + skCase.acquireSingleUserCaseReadLock(); + String getAllTagSetsQuery = "SELECT * FROM tsk_tag_sets"; + try (Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(getAllTagSetsQuery);) { + while (resultSet.next()) { + int setID = resultSet.getInt("tag_set_id"); + String setName = resultSet.getString("name"); + TagSet set = new TagSet(setID, setName, getTagNamesByTagSetID(setID)); + tagSetList.add(set); + } + } catch (SQLException ex) { + throw new TskCoreException("Error occurred getting TagSet list.", ex); + } finally { + connection.close(); + skCase.releaseSingleUserCaseReadLock(); + } + return tagSetList; + } + + /** + * Inserts a row into the tag_sets table in the case database. + * + * @param name The tag set name. + * @param tagNames + * + * @return A TagSet object for the new row. + * + * @throws TskCoreException + */ + public TagSet addTagSet(String name, List<TagName> tagNames) throws TskCoreException { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("Error adding TagSet, TagSet name must be non-empty string."); + } + + TagSet tagSet = null; + + CaseDbConnection connection = skCase.getConnection(); + skCase.acquireSingleUserCaseWriteLock(); + try (Statement stmt = connection.createStatement()) { + connection.beginTransaction(); + String query = String.format("INSERT INTO tsk_tag_sets (name) VALUES('%s')", name); + + if (skCase.getDatabaseType() == POSTGRESQL) { + stmt.execute(query, Statement.RETURN_GENERATED_KEYS); + } else { + stmt.execute(query); + } + + try (ResultSet resultSet = stmt.getGeneratedKeys()) { + + resultSet.next(); + int setID = resultSet.getInt(1); + + List<TagName> updatedTags = new ArrayList<>(); + if (tagNames != null) { + // Get all of the TagName ids they can be updated in one + // SQL call. + List<String> idList = new ArrayList<>(); + for (TagName tagName : tagNames) { + idList.add(Long.toString(tagName.getId())); + } + + stmt.executeUpdate(String.format("UPDATE tag_names SET tag_set_id = %d WHERE tag_name_id IN (%s)", setID, String.join(",", idList))); + + for (TagName tagName : tagNames) { + updatedTags.add(new TagName(tagName.getId(), + tagName.getDisplayName(), + tagName.getDescription(), + tagName.getColor(), + tagName.getKnownStatus(), + setID)); + } + } + tagSet = new TagSet(setID, name, updatedTags); + } + connection.commitTransaction(); + } catch (SQLException ex) { + connection.rollbackTransaction(); + throw new TskCoreException(String.format("Error adding tag set %s", name), ex); + } finally { + connection.close(); + skCase.releaseSingleUserCaseWriteLock(); + } + + return tagSet; + } + + /** + * Remove a row from the tag set table. The TagNames in the TagSet will not + * be deleted, nor will any tags with the TagNames from the deleted tag set + * be deleted. + * + * @param tagSet TagSet to be deleted + * + * @throws TskCoreException + */ + public void deleteTagSet(TagSet tagSet) throws TskCoreException { + if (tagSet == null) { + throw new IllegalArgumentException("Error adding deleting TagSet, TagSet object was null"); + } + + CaseDbConnection connection = skCase.getConnection(); + skCase.acquireSingleUserCaseWriteLock(); + try (Statement stmt = connection.createStatement()) { + connection.beginTransaction(); + String queryTemplate = "DELETE FROM tsk_tag_sets WHERE tag_set_id = '%d'"; + stmt.execute(String.format(queryTemplate, tagSet.getId())); + connection.commitTransaction(); + } catch (SQLException ex) { + connection.rollbackTransaction(); + throw new TskCoreException(String.format("Error deleting tag set where id = %d.", tagSet.getId()), ex); + } finally { + connection.close(); + skCase.releaseSingleUserCaseWriteLock(); + } + } + + /** + * Add the given TagName to the TagSet. + * + * @param tagSet The tag set being added to. + * @param tagName The tag name to add to the set. + * + * @return TagSet TagSet object with newly added TagName. + * + * @throws TskCoreException + */ + public TagSet addTagNameToTagSet(TagSet tagSet, TagName tagName) throws TskCoreException { + if (tagSet == null || tagName == null) { + throw new IllegalArgumentException("NULL value passed to addTagToTagSet"); + } + + // Make sure the tagName is not already in the list. + List<TagName> setTagNameList = tagSet.getTagNames(); + for (TagName tag : setTagNameList) { + if (tagName.getId() == tag.getId()) { + return tagSet; + } + } + + CaseDbConnection connection = skCase.getConnection(); + skCase.acquireSingleUserCaseWriteLock(); + + try (Statement stmt = connection.createStatement()) { + connection.beginTransaction(); + + String queryTemplate = "UPDATE tag_names SET tag_set_id = %d where tag_name_id = %d"; + stmt.executeUpdate(String.format(queryTemplate, tagSet.getId(), tagName.getId())); + + connection.commitTransaction(); + + List<TagName> newTagNameList = new ArrayList<>(); + newTagNameList.addAll(setTagNameList); + newTagNameList.add(new TagName(tagName.getId(), tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus(), tagSet.getId())); + + return new TagSet(tagSet.getId(), tagSet.getName(), newTagNameList); + + } catch (SQLException ex) { + connection.rollbackTransaction(); + throw new TskCoreException(String.format("Error adding TagName (id=%d) to TagSet (id=%s)", tagName.getId(), tagSet.getId()), ex); + } finally { + connection.close(); + skCase.releaseSingleUserCaseWriteLock(); + } + } + + /** + * Inserts a row into the blackboard_artifact_tags table in the case + * database. + * + * @param artifact The blackboard artifact to tag. + * @param tagName The name to use for the tag. + * @param comment A comment to store with the tag. + * + * @return A BlackboardArtifactTag data transfer object (DTO) for the new + * row. + * + * @throws TskCoreException + */ + public BlackboardArtifactTagChange addArtifactTag(BlackboardArtifact artifact, TagName tagName, String comment) throws TskCoreException { + if (artifact == null || tagName == null) { + throw new IllegalArgumentException("NULL argument passed to addArtifactTag"); + } + + CaseDbConnection connection = skCase.getConnection(); + skCase.acquireSingleUserCaseWriteLock(); + List<BlackboardArtifactTag> removedTags = new ArrayList<>(); + List<String> removedTagIds = new ArrayList<>(); + try { + connection.beginTransaction(); + // If a TagName is part of a TagSet remove any existing tags from the + // set that are currenctly on the artifact + long tagSetId = tagName.getTagSetId(); + if (tagSetId > 0) { + // Get the list of all of the blackboardArtifactTags that use + // TagName for the given artifact. + String selectQuery = String.format("SELECT * from blackboard_artifact_tags JOIN tag_names ON tag_names.tag_name_id = blackboard_artifact_tags.tag_name_id JOIN tsk_examiners on tsk_examiners.examiner_id = blackboard_artifact_tags.examiner_id WHERE artifact_id = %d AND tag_names.tag_set_id = %d", artifact.getArtifactID(), tagSetId); + + try (Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(selectQuery)) { + while (resultSet.next()) { + BlackboardArtifactTag bat + = new BlackboardArtifactTag(resultSet.getLong("tag_id"), + artifact, + skCase.getContentById(artifact.getObjectID()), + tagName, + resultSet.getString("comment"), + resultSet.getString("login_name")); + + removedTags.add(bat); + removedTagIds.add(Long.toString(bat.getId())); + } + } + + if (!removedTags.isEmpty()) { + // Remove the tags. + String removeQuery = String.format("DELETE FROM blackboard_artifact_tags WHERE tag_id IN (%s)", String.join(",", removedTagIds)); + try (Statement stmt = connection.createStatement()) { + stmt.executeUpdate(removeQuery); + } + } + } + + // Add the new Tag. + BlackboardArtifactTag artifactTag = null; + try (Statement stmt = connection.createStatement()) { + Examiner currentExaminer = skCase.getCurrentExaminer(); + String query = String.format( + "INSERT INTO blackboard_artifact_tags (artifact_id, tag_name_id, comment, examiner_id) VALUES (%d, %d, '%s', %d)", + artifact.getArtifactID(), + tagName.getId(), + comment, + currentExaminer.getId()); + + if (skCase.getDatabaseType() == POSTGRESQL) { + stmt.execute(query, Statement.RETURN_GENERATED_KEYS); + } else { + stmt.execute(query); + } + + try (ResultSet resultSet = stmt.getGeneratedKeys()) { + resultSet.next(); + artifactTag = new BlackboardArtifactTag(resultSet.getLong(1), //last_insert_rowid() + artifact, skCase.getContentById(artifact.getObjectID()), tagName, comment, currentExaminer.getLoginName()); + } + } + + connection.commitTransaction(); + + return new BlackboardArtifactTagChange(artifactTag, removedTags); + } catch (SQLException ex) { + connection.rollbackTransaction(); + throw new TskCoreException("Error adding row to blackboard_artifact_tags table (obj_id = " + artifact.getArtifactID() + ", tag_name_id = " + tagName.getId() + ")", ex); + } finally { + + connection.close(); + skCase.releaseSingleUserCaseWriteLock(); + } + } + + /** + * Inserts a row into the content_tags table in the case database. + * + * @param content The content to tag. + * @param tagName The name to use for the tag. + * @param comment A comment to store with the tag. + * @param beginByteOffset Designates the beginning of a tagged section. + * @param endByteOffset Designates the end of a tagged section. + * + * @return A ContentTag data transfer object (DTO) for the new row. + * + * @throws TskCoreException + */ + public ContentTagChange addContentTag(Content content, TagName tagName, String comment, long beginByteOffset, long endByteOffset) throws TskCoreException { + CaseDbConnection connection = skCase.getConnection(); + List<ContentTag> removedTags = new ArrayList<>(); + List<String> removedTagIds = new ArrayList<>(); + skCase.acquireSingleUserCaseWriteLock(); + try { + connection.beginTransaction(); + long tagSetId = tagName.getTagSetId(); + + if (tagSetId > 0) { + String selectQuery = String.format("SELECT * from content_tags JOIN tag_names ON tag_names.tag_name_id = content_tags.tag_name_id JOIN tsk_examiners on tsk_examiners.examiner_id = content_tags.examiner_id WHERE obj_id = %d AND tag_names.tag_set_id = %d", content.getId(), tagSetId); + + try (Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(selectQuery)) { + while (resultSet.next()) { + ContentTag bat + = new ContentTag(resultSet.getLong("tag_id"), + content, + tagName, + resultSet.getString("comment"), + resultSet.getLong("begin_byte_offset"), + resultSet.getLong("end_byte_offset"), + resultSet.getString("login_name")); + + removedTagIds.add(Long.toString(bat.getId())); + removedTags.add(bat); + } + } + + if (!removedTags.isEmpty()) { + String removeQuery = String.format("DELETE FROM content_tags WHERE tag_id IN (%s)", String.join(",", removedTagIds)); + try (Statement stmt = connection.createStatement()) { + stmt.executeUpdate(removeQuery); + } + } + } + + 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()) { + Examiner currentExaminer = skCase.getCurrentExaminer(); + String query = String.format(queryTemplate, + content.getId(), + tagName.getId(), + comment, + beginByteOffset, + endByteOffset, + currentExaminer.getId()); + + if (skCase.getDatabaseType() == POSTGRESQL) { + stmt.executeUpdate(query, Statement.RETURN_GENERATED_KEYS); + } else { + stmt.executeUpdate(query); + } + + try (ResultSet resultSet = stmt.getGeneratedKeys()) { + resultSet.next(); + contentTag = new ContentTag(resultSet.getLong(1), //last_insert_rowid() + content, tagName, comment, beginByteOffset, endByteOffset, currentExaminer.getLoginName()); + } + } + + connection.commitTransaction(); + return new ContentTagChange(contentTag, removedTags); + } catch (SQLException ex) { + connection.rollbackTransaction(); + throw new TskCoreException("Error adding row to content_tags table (obj_id = " + content.getId() + ", tag_name_id = " + tagName.getId() + ")", ex); + } finally { + connection.close(); + skCase.releaseSingleUserCaseWriteLock(); + } + } + + /** + * Returns a list of all of the TagNames that are apart of the given TagSet. + * + * @param tagSetId ID of a TagSet. + * + * @return List of TagNames for the TagSet or empty list if none were found. + * + * @throws TskCoreException + */ + private List<TagName> getTagNamesByTagSetID(int tagSetId) throws TskCoreException { + + if (tagSetId <= 0) { + throw new IllegalArgumentException("Invalid tagSetID passed to getTagNameByTagSetID"); + } + + List<TagName> tagNameList = new ArrayList<>(); + + CaseDbConnection connection = skCase.getConnection(); + + skCase.acquireSingleUserCaseReadLock(); + String query = String.format("SELECT * FROM tag_names WHERE tag_set_id = %d", tagSetId); + try (Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(query)) { + while (resultSet.next()) { + long tagId = resultSet.getLong("tag_name_id"); + String tagName = resultSet.getString("display_name"); + String description = resultSet.getString("description"); + String color = resultSet.getString("color"); + byte knownStatus = resultSet.getByte("knownStatus"); + + tagNameList.add(new TagName(tagId, tagName, description, TagName.HTML_COLOR.getColorByName(color), TskData.FileKnown.valueOf(knownStatus), tagSetId)); + } + } catch (SQLException ex) { + throw new TskCoreException(String.format("Error getting tag names for tag set (%d)", tagSetId), ex); + } finally { + connection.close(); + skCase.releaseSingleUserCaseReadLock(); + } + + return tagNameList; + } + + /** + * Object to store the tag change from a call to addArtifactTag. + */ + public static class BlackboardArtifactTagChange { + + private final BlackboardArtifactTag addedTag; + private final List<BlackboardArtifactTag> removedTagList; + + /** + * Construct a new artifact tag change object. + * + * @param added Newly created artifact tag. + * @param removed List of removed tags. + */ + BlackboardArtifactTagChange(BlackboardArtifactTag added, List<BlackboardArtifactTag> removed) { + this.addedTag = added; + this.removedTagList = removed; + } + + /** + * Returns the newly created tag. + * + * @return Add artifact tag. + */ + public BlackboardArtifactTag getAddedTag() { + return addedTag; + } + + /** + * Returns a list of the artifacts tags that were removed. + * + * @return + */ + public List<BlackboardArtifactTag> getRemovedTags() { + return Collections.unmodifiableList(removedTagList); + } + } + + /** + * Object to store the tag change from a call to addContentTag. + */ + public static class ContentTagChange { + + private final ContentTag addedTag; + private final List<ContentTag> removedTagList; + + /** + * Construct a new content tag change object. + * + * @param added Newly created artifact tag. + * @param removed List of removed tags. + */ + ContentTagChange(ContentTag added, List<ContentTag> removed) { + this.addedTag = added; + this.removedTagList = removed; + } + + /** + * Returns the newly created tag. + * + * @return Add artifact tag. + */ + public ContentTag getAddedTag() { + return addedTag; + } + + /** + * Returns a list of the artifacts tags that were removed. + * + * @return + */ + public List<ContentTag> getRemovedTags() { + return Collections.unmodifiableList(removedTagList); + } + } +} diff --git a/tsk/auto/db_sqlite.cpp b/tsk/auto/db_sqlite.cpp index 2a0e8868f9ef1d5eee4e4726627b772c27ba3655..5625c5ad21207d7754d9616559b41930ce89d1f3 100644 --- a/tsk/auto/db_sqlite.cpp +++ b/tsk/auto/db_sqlite.cpp @@ -338,8 +338,11 @@ TskDbSqlite::initialize() ("CREATE TABLE tsk_files_derived_method (derived_id INTEGER PRIMARY KEY, tool_name TEXT NOT NULL, tool_version TEXT NOT NULL, other TEXT)", "Error creating tsk_files_derived_method table: %s\n") || + attempt_exec + ("CREATE TABLE tsk_tag_sets (tag_set_id INTEGER PRIMARY KEY, name TEXT UNIQUE)", "Error creating tsk_tag_sets table: %s\n") + || attempt_exec - ("CREATE TABLE tag_names (tag_name_id INTEGER PRIMARY KEY, display_name TEXT UNIQUE, description TEXT NOT NULL, color TEXT NOT NULL, knownStatus INTEGER NOT NULL)", + ("CREATE TABLE tag_names (tag_name_id INTEGER PRIMARY KEY, display_name TEXT UNIQUE, description TEXT NOT NULL, color TEXT NOT NULL, knownStatus INTEGER NOT NULL, tag_set_id INTEGER, FOREIGN KEY(tag_set_id) REFERENCES tsk_tag_sets(tag_set_id) ON DELETE SET NULL)", "Error creating tag_names table: %s\n") || attempt_exec("CREATE TABLE review_statuses (review_status_id INTEGER PRIMARY KEY, " diff --git a/tsk/auto/tsk_db.h b/tsk/auto/tsk_db.h index 4497d30c8430bca6a9e302d5a484127168ee782c..096764acb30ad4047d5d4c860443b4e5c36ba16a 100755 --- a/tsk/auto/tsk_db.h +++ b/tsk/auto/tsk_db.h @@ -31,7 +31,7 @@ using std::string; * Keep these values in sync with CURRENT_DB_SCHEMA_VERSION in SleuthkitCase.java */ #define TSK_SCHEMA_VER 8 -#define TSK_SCHEMA_MINOR_VER 4 +#define TSK_SCHEMA_MINOR_VER 5 /** * Values for the type column in the tsk_objects table. diff --git a/tsk/fs/ext2fs.c b/tsk/fs/ext2fs.c index 45dd18c277f241a06b10d5aca10a3d005431b127..1f56c943021b848a6047e11b73749e198b51957f 100755 --- a/tsk/fs/ext2fs.c +++ b/tsk/fs/ext2fs.c @@ -841,6 +841,20 @@ ext2fs_dinode_copy(EXT2FS_INFO * ext2fs, TSK_FS_META * fs_meta, grp_num * tsk_getu32(fs->endian, ext2fs->fs->s_inodes_per_group) + fs->first_inum; + + /* + * Ensure that inum - ibase refers to a valid bit offset in imap_buf. + */ + if ((inum - ibase) > fs->block_size*8) { + tsk_release_lock(&ext2fs->lock); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); + tsk_error_set_errstr("ext2fs_dinode_copy: Invalid offset into imap_buf (inum %" PRIuINUM " - ibase %" PRIuINUM ")", + inum, ibase); + return 1; + } + + /* * Apply the allocated/unallocated restriction. */ @@ -1052,9 +1066,9 @@ ext2fs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum, ext2fs->fs->s_inodes_per_group) + 1; /* - * Ensure that inum - ibase refers to a valid offset in imap_buf. + * Ensure that inum - ibase refers to a valid bit offset in imap_buf. */ - if ((inum - ibase) > fs->block_size) { + if ((inum - ibase) > fs->block_size*8) { tsk_release_lock(&ext2fs->lock); free(dino_buf); tsk_error_reset();