Sleuth Kit Java Bindings (JNI) 4.14.0
Java bindings for using The Sleuth Kit
Loading...
Searching...
No Matches
Blackboard.java
Go to the documentation of this file.
1/*
2 * Sleuth Kit Data Model
3 *
4 * Copyright 2018-2021 Basis Technology Corp.
5 * Contact: carrier <at> sleuthkit <dot> org
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19package org.sleuthkit.datamodel;
20
21import com.google.common.annotations.Beta;
22import com.google.common.collect.ImmutableSet;
23import java.sql.PreparedStatement;
24import java.sql.ResultSet;
25import java.sql.SQLException;
26import java.sql.Statement;
27import java.text.MessageFormat;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.Collection;
31import java.util.Collections;
32import java.util.HashMap;
33import java.util.HashSet;
34import java.util.List;
35import java.util.Map;
36import java.util.Objects;
37import java.util.Optional;
38import java.util.Set;
39import java.util.concurrent.ConcurrentHashMap;
40import java.util.logging.Level;
41import java.util.logging.Logger;
42import java.util.stream.Collectors;
43import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection;
44import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
45import static org.sleuthkit.datamodel.SleuthkitCase.closeConnection;
46import static org.sleuthkit.datamodel.SleuthkitCase.closeResultSet;
47import static org.sleuthkit.datamodel.SleuthkitCase.closeStatement;
48
53public final class Blackboard {
54
55 private static final Logger LOGGER = Logger.getLogger(Blackboard.class.getName());
56
57 /*
58 * ConcurrentHashMap semantics are fine for these caches to which entries
59 * are added, but never removed. There is also no need to keep each pair of
60 * related caches strictly consistent with each other, because cache misses
61 * will be extremely rare (standard types are loaded when the case is
62 * opened), and the cost of a cache miss is low.
63 */
64 private final Map<Integer, BlackboardArtifact.Type> typeIdToArtifactTypeMap = new ConcurrentHashMap<>();
65 private final Map<Integer, BlackboardAttribute.Type> typeIdToAttributeTypeMap = new ConcurrentHashMap<>();
66 private final Map<String, BlackboardArtifact.Type> typeNameToArtifactTypeMap = new ConcurrentHashMap<>();
67 private final Map<String, BlackboardAttribute.Type> typeNameToAttributeTypeMap = new ConcurrentHashMap<>();
68
69 static final int MIN_USER_DEFINED_TYPE_ID = 10000;
70
71 private final SleuthkitCase caseDb;
72
79 Blackboard(SleuthkitCase casedb) {
80 this.caseDb = Objects.requireNonNull(casedb, "Cannot create Blackboard for null SleuthkitCase");
81 }
82
98 @Deprecated
99 public void postArtifact(BlackboardArtifact artifact, String moduleName) throws BlackboardException {
100 postArtifacts(Collections.singleton(artifact), moduleName, null);
101 }
102
118 @Deprecated
119 public void postArtifacts(Collection<BlackboardArtifact> artifacts, String moduleName) throws BlackboardException {
120 postArtifacts(artifacts, moduleName, null);
121 }
122
138 public void postArtifact(BlackboardArtifact artifact, String moduleName, Long ingestJobId) throws BlackboardException {
139 postArtifacts(Collections.singleton(artifact), moduleName, ingestJobId);
140 }
141
157 public void postArtifacts(Collection<BlackboardArtifact> artifacts, String moduleName, Long ingestJobId) throws BlackboardException {
158 for (BlackboardArtifact artifact : artifacts) {
159 try {
160 caseDb.getTimelineManager().addArtifactEvents(artifact);
161 } catch (TskCoreException ex) {
162 throw new BlackboardException(String.format("Failed to add events to timeline for artifact '%s'", artifact), ex);
163 }
164 }
165 caseDb.fireTSKEvent(new ArtifactsPostedEvent(artifacts, moduleName, ingestJobId));
166 }
167
182 public BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName) throws BlackboardException {
183 return getOrAddArtifactType(typeName, displayName, BlackboardArtifact.Category.DATA_ARTIFACT);
184 }
185
199 public BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName, BlackboardArtifact.Category category) throws BlackboardException {
200 if (category == null) {
201 throw new BlackboardException("Category provided must be non-null");
202 }
203
204 if (typeNameToArtifactTypeMap.containsKey(typeName)) {
205 return typeNameToArtifactTypeMap.get(typeName);
206 }
207
208 CaseDbTransaction trans = null;
209 try {
210 trans = caseDb.beginTransaction();
211
212 CaseDbConnection connection = trans.getConnection();
213
214 // try to find preexisting type id in database
215 boolean typeFound;
216 try (PreparedStatement findCurrPrepState = connection.prepareStatement(
217 "SELECT artifact_type_id FROM blackboard_artifact_types WHERE type_name = ?",
218 Statement.RETURN_GENERATED_KEYS)) {
219 findCurrPrepState.setString(1, typeName);
220
221 try (ResultSet rs = connection.executeQuery(findCurrPrepState)) {
222 typeFound = rs.next();
223 }
224 }
225
226 if (!typeFound) {
227 // if not found, get next id
228 int maxID = 0;
229 try (Statement getNextIdState = connection.createStatement();
230 ResultSet rs = connection.executeQuery(getNextIdState,
231 "SELECT MAX(artifact_type_id) AS highest_id FROM blackboard_artifact_types")) {
232 if (rs.next()) {
233 maxID = rs.getInt("highest_id");
234 if (maxID < MIN_USER_DEFINED_TYPE_ID) {
235 maxID = MIN_USER_DEFINED_TYPE_ID;
236 } else {
237 maxID++;
238 }
239 }
240 }
241
242 // insert the type
243 try (PreparedStatement insertItemPrepState = connection.prepareStatement(
244 "INSERT INTO blackboard_artifact_types (artifact_type_id, type_name, display_name, category_type) VALUES (?, ?, ?, ?)",
245 Statement.RETURN_GENERATED_KEYS)) {
246 insertItemPrepState.setInt(1, maxID);
247 insertItemPrepState.setString(2, typeName);
248 insertItemPrepState.setString(3, displayName);
249 insertItemPrepState.setInt(4, category.getID());
250
251 insertItemPrepState.executeUpdate();
252
253 }
254
255 // cache the type in memory
256 BlackboardArtifact.Type type = new BlackboardArtifact.Type(maxID, typeName, displayName, category);
257 this.typeIdToArtifactTypeMap.put(type.getTypeID(), type);
258 this.typeNameToArtifactTypeMap.put(type.getTypeName(), type);
259 trans.commit();
260 trans = null;
261 return type;
262 } else {
263 // if preexisting, return what we have
264 trans.commit();
265 trans = null;
266 try {
267 return getArtifactType(typeName);
268 } catch (TskCoreException ex) {
269 throw new BlackboardException("Failed to get or add artifact type: " + typeName, ex);
270 }
271 }
272 } catch (SQLException | TskCoreException ex) {
273 try {
274 if (trans != null) {
275 trans.rollback();
276 trans = null;
277 }
278 } catch (TskCoreException ex2) {
279 LOGGER.log(Level.SEVERE, "Error rolling back transaction", ex2);
280 }
281 throw new BlackboardException("Error adding artifact type: " + typeName, ex);
282 } finally {
283 if (trans != null) {
284 try {
285 trans.rollback();
286 } catch (TskCoreException ex) {
287 throw new BlackboardException("Error rolling back transaction", ex);
288 }
289 }
290 }
291 }
292
303 public BlackboardAttribute.Type getAttributeType(String attrTypeName) throws TskCoreException {
304 if (this.typeNameToAttributeTypeMap.containsKey(attrTypeName)) {
305 return this.typeNameToAttributeTypeMap.get(attrTypeName);
306 }
307 CaseDbConnection connection = null;
308 Statement s = null;
309 ResultSet rs = null;
310 caseDb.acquireSingleUserCaseReadLock();
311 try {
312 connection = caseDb.getConnection();
313 s = connection.createStatement();
314 rs = connection.executeQuery(s, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types WHERE type_name = '" + attrTypeName + "'"); //NON-NLS
315 BlackboardAttribute.Type type = null;
316 if (rs.next()) {
317 type = new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"),
318 rs.getString("display_name"), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type")));
319 this.typeIdToAttributeTypeMap.put(type.getTypeID(), type);
320 this.typeNameToAttributeTypeMap.put(attrTypeName, type);
321 }
322 return type;
323 } catch (SQLException ex) {
324 throw new TskCoreException("Error getting attribute type id", ex);
325 } finally {
326 closeResultSet(rs);
327 closeStatement(s);
328 closeConnection(connection);
329 caseDb.releaseSingleUserCaseReadLock();
330 }
331 }
332
344 if (this.typeIdToAttributeTypeMap.containsKey(typeID)) {
345 return this.typeIdToAttributeTypeMap.get(typeID);
346 }
347 CaseDbConnection connection = null;
348 Statement s = null;
349 ResultSet rs = null;
351 try {
352 connection = caseDb.getConnection();
353 s = connection.createStatement();
354 rs = connection.executeQuery(s, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types WHERE attribute_type_id = " + typeID + ""); //NON-NLS
355 BlackboardAttribute.Type type = null;
356 if (rs.next()) {
357 type = new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"),
358 rs.getString("display_name"), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type")));
359 this.typeIdToAttributeTypeMap.put(typeID, type);
360 this.typeNameToAttributeTypeMap.put(type.getTypeName(), type);
361 }
362 return type;
363 } catch (SQLException ex) {
364 throw new TskCoreException("Error getting attribute type id", ex);
365 } finally {
366 closeResultSet(rs);
367 closeStatement(s);
368 closeConnection(connection);
369 caseDb.releaseSingleUserCaseReadLock();
370 }
371 }
372
383 public BlackboardArtifact.Type getArtifactType(String artTypeName) throws TskCoreException {
384 if (this.typeNameToArtifactTypeMap.containsKey(artTypeName)) {
385 return this.typeNameToArtifactTypeMap.get(artTypeName);
386 }
387
388 caseDb.acquireSingleUserCaseReadLock();
389 try (CaseDbConnection connection = caseDb.getConnection(); PreparedStatement getTypePrepState = connection.prepareStatement(
390 "SELECT artifact_type_id, type_name, display_name, category_type FROM blackboard_artifact_types WHERE type_name = ?",
391 Statement.RETURN_GENERATED_KEYS)) {
392 getTypePrepState.setString(1, artTypeName);
393
394 try (ResultSet rs = getTypePrepState.executeQuery()) {
395 BlackboardArtifact.Type type = null;
396 if (rs.next()) {
397 type = new BlackboardArtifact.Type(rs.getInt("artifact_type_id"),
398 rs.getString("type_name"), rs.getString("display_name"),
399 BlackboardArtifact.Category.fromID(rs.getInt("category_type")));
400 this.typeIdToArtifactTypeMap.put(type.getTypeID(), type);
401 this.typeNameToArtifactTypeMap.put(artTypeName, type);
402 }
403 return type;
404 }
405 } catch (SQLException ex) {
406 throw new TskCoreException("Error getting artifact type from the database", ex);
407 } finally {
408 caseDb.releaseSingleUserCaseReadLock();
409 }
410 }
411
424 if (this.typeIdToArtifactTypeMap.containsKey(artTypeId)) {
425 return typeIdToArtifactTypeMap.get(artTypeId);
426 }
427 CaseDbConnection connection = null;
428 Statement s = null;
429 ResultSet rs = null;
430 caseDb.acquireSingleUserCaseReadLock();
431 try {
432 connection = caseDb.getConnection();
433 s = connection.createStatement();
434 rs = connection.executeQuery(s, "SELECT artifact_type_id, type_name, display_name, category_type FROM blackboard_artifact_types WHERE artifact_type_id = " + artTypeId + ""); //NON-NLS
435 BlackboardArtifact.Type type = null;
436 if (rs.next()) {
437 type = new BlackboardArtifact.Type(rs.getInt("artifact_type_id"),
438 rs.getString("type_name"), rs.getString("display_name"),
439 BlackboardArtifact.Category.fromID(rs.getInt("category_type")));
440 this.typeIdToArtifactTypeMap.put(artTypeId, type);
441 this.typeNameToArtifactTypeMap.put(type.getTypeName(), type);
442 return type;
443 } else {
444 throw new TskCoreException("No artifact type found matching id: " + artTypeId);
445 }
446 } catch (SQLException ex) {
447 throw new TskCoreException("Error getting artifact type from the database", ex);
448 } finally {
449 closeResultSet(rs);
450 closeStatement(s);
451 closeConnection(connection);
452 caseDb.releaseSingleUserCaseReadLock();
453 }
454 }
455
465 public ArrayList<BlackboardAttribute> getBlackboardAttributes(final BlackboardArtifact artifact) throws TskCoreException {
466 CaseDbConnection connection = null;
467 Statement statement = null;
468 ResultSet rs = null;
469
470 String rowId;
471 switch (caseDb.getDatabaseType()) {
472 case POSTGRESQL:
473 rowId = "attrs.CTID";
474 break;
475 case SQLITE:
476 rowId = "attrs.ROWID";
477 break;
478 default:
479 throw new TskCoreException("Unknown database type: " + caseDb.getDatabaseType());
480 }
481
482 caseDb.acquireSingleUserCaseReadLock();
483 try {
484 connection = caseDb.getConnection();
485 statement = connection.createStatement();
486 rs = connection.executeQuery(statement, "SELECT attrs.artifact_id AS artifact_id, "
487 + "attrs.source AS source, attrs.context AS context, attrs.attribute_type_id AS attribute_type_id, "
488 + "attrs.value_type AS value_type, attrs.value_byte AS value_byte, "
489 + "attrs.value_text AS value_text, attrs.value_int32 AS value_int32, "
490 + "attrs.value_int64 AS value_int64, attrs.value_double AS value_double, "
491 + "types.type_name AS type_name, types.display_name AS display_name "
492 + "FROM blackboard_attributes AS attrs, blackboard_attribute_types AS types WHERE attrs.artifact_id = " + artifact.getArtifactID()
493 + " AND attrs.attribute_type_id = types.attribute_type_id "
494 + " ORDER BY " + rowId);
495 ArrayList<BlackboardAttribute> attributes = new ArrayList<>();
496 while (rs.next()) {
497 final BlackboardAttribute attr = createAttributeFromResultSet(rs);
498 attr.setParentDataSourceID(artifact.getDataSourceObjectID());
499 attributes.add(attr);
500 }
501 return attributes;
502 } catch (SQLException ex) {
503 throw new TskCoreException("Error getting attributes for artifact, artifact id = " + artifact.getArtifactID(), ex);
504 } finally {
505 closeResultSet(rs);
506 closeStatement(statement);
507 closeConnection(connection);
508 caseDb.releaseSingleUserCaseReadLock();
509 }
510 }
511
522 @Beta
523 public <T extends BlackboardArtifact> void loadBlackboardAttributes(List<T> arts) throws TskCoreException {
524
525 if (arts.isEmpty()) {
526 return;
527 }
528
529 // Make a map of artifact ID to artifact
530 Map<Long, BlackboardArtifact> artifactMap = new HashMap<>();
531 for (BlackboardArtifact art : arts) {
532 artifactMap.put(art.getArtifactID(), art);
533 }
534
535 // Make a map of artifact ID to attribute list
536 Map<Long, List<BlackboardAttribute>> attributeMap = new HashMap<>();
537
538 // Get all artifact IDs as a comma-separated string
539 String idString = arts.stream().map(p -> Long.toString(p.getArtifactID())).collect(Collectors.joining(", "));
540
541 String rowId;
542 switch (caseDb.getDatabaseType()) {
543 case POSTGRESQL:
544 rowId = "attrs.CTID";
545 break;
546 case SQLITE:
547 rowId = "attrs.ROWID";
548 break;
549 default:
550 throw new TskCoreException("Unknown database type: " + caseDb.getDatabaseType());
551 }
552
553 // Get the attributes
554 CaseDbConnection connection = null;
555 Statement statement = null;
556 ResultSet rs = null;
558 try {
559 connection = caseDb.getConnection();
560 statement = connection.createStatement();
561 rs = connection.executeQuery(statement, "SELECT attrs.artifact_id AS artifact_id, "
562 + "attrs.source AS source, attrs.context AS context, attrs.attribute_type_id AS attribute_type_id, "
563 + "attrs.value_type AS value_type, attrs.value_byte AS value_byte, "
564 + "attrs.value_text AS value_text, attrs.value_int32 AS value_int32, "
565 + "attrs.value_int64 AS value_int64, attrs.value_double AS value_double, "
566 + "types.type_name AS type_name, types.display_name AS display_name "
567 + "FROM blackboard_attributes AS attrs, blackboard_attribute_types AS types WHERE attrs.artifact_id IN (" + idString + ") "
568 + " AND attrs.attribute_type_id = types.attribute_type_id"
569 + " ORDER BY " + rowId);
570 while (rs.next()) {
571 final BlackboardAttribute attr = createAttributeFromResultSet(rs);
572 attr.setParentDataSourceID(artifactMap.get(attr.getArtifactID()).getDataSourceObjectID());
573
574 // Collect the list of attributes for each artifact
575 if (!attributeMap.containsKey(attr.getArtifactID())) {
576 attributeMap.put(attr.getArtifactID(), new ArrayList<>());
577 }
578 attributeMap.get(attr.getArtifactID()).add(attr);
579 }
580
581 // Save the attributes to the artifacts
582 for (Long artifactID : attributeMap.keySet()) {
583 artifactMap.get(artifactID).setAttributes(attributeMap.get(artifactID));
584 }
585
586 } catch (SQLException ex) {
587 throw new TskCoreException("Error loading attributes", ex);
588 } finally {
589 closeResultSet(rs);
590 closeStatement(statement);
591 closeConnection(connection);
592 caseDb.releaseSingleUserCaseReadLock();
593 }
594 }
595
604 private BlackboardAttribute createAttributeFromResultSet(ResultSet rs) throws SQLException {
605 int attributeTypeId = rs.getInt("attribute_type_id");
606 String attributeTypeName = rs.getString("type_name");
607 BlackboardAttribute.Type attributeType;
608 if (this.typeIdToAttributeTypeMap.containsKey(attributeTypeId)) {
609 attributeType = this.typeIdToAttributeTypeMap.get(attributeTypeId);
610 } else {
611 attributeType = new BlackboardAttribute.Type(attributeTypeId, attributeTypeName,
612 rs.getString("display_name"),
613 BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getInt("value_type")));
614 this.typeIdToAttributeTypeMap.put(attributeTypeId, attributeType);
615 this.typeNameToAttributeTypeMap.put(attributeTypeName, attributeType);
616 }
617
618 return new BlackboardAttribute(
619 rs.getLong("artifact_id"),
620 attributeType,
621 rs.getString("source"),
622 rs.getString("context"),
623 rs.getInt("value_int32"),
624 rs.getLong("value_int64"),
625 rs.getDouble("value_double"),
626 rs.getString("value_text"),
627 rs.getBytes("value_byte"), caseDb
628 );
629 }
630
640 @Beta
641 public void updateFileAttributes(long fileObjId, List<Attribute> attributes) throws TskCoreException {
642
643 caseDb.acquireSingleUserCaseWriteLock();
644 try (CaseDbConnection connection = caseDb.getConnection()) {
645 for (Attribute attr : attributes) {
646 String updateString = "UPDATE tsk_file_attributes SET value_byte = ?, value_text = ?, value_int32 = ?, "
647 + " value_int64 = ?, value_double = ? WHERE attribute_type_id = " + attr.getAttributeType().getTypeID()
648 + " AND obj_id = " + fileObjId;
649
650 try (PreparedStatement preparedStatement = connection.getPreparedStatement(updateString, Statement.NO_GENERATED_KEYS);) {
651 preparedStatement.clearParameters();
652
653 if (attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE) {
654 preparedStatement.setBytes(1, attr.getValueBytes());
655 } else {
656 preparedStatement.setBytes(1, null);
657 }
658
659 if (attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING
660 || attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
661 preparedStatement.setString(2, attr.getValueString());
662 } else {
663 preparedStatement.setString(2, null);
664 }
665
666 if (attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.INTEGER) {
667 preparedStatement.setInt(3, attr.getValueInt());
668 } else {
669 preparedStatement.setNull(3, java.sql.Types.INTEGER);
670 }
671
672 if (attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME
673 || attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.LONG) {
674 preparedStatement.setLong(4, attr.getValueLong());
675 } else {
676 preparedStatement.setNull(4, java.sql.Types.BIGINT);
677 }
678
679 if (attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE) {
680 preparedStatement.setDouble(5, attr.getValueDouble());
681 } else {
682 preparedStatement.setNull(5, java.sql.Types.DOUBLE);
683 }
684
685 connection.executeUpdate(preparedStatement);
686
687 } catch (SQLException ex) {
688 throw new TskCoreException(String.format("Error updating attribute using query = '%s'", updateString), ex);
689 }
690 }
691 } finally {
692 caseDb.releaseSingleUserCaseWriteLock();
693 }
694 }
695
705 ArrayList<Attribute> getFileAttributes(final AbstractFile file) throws TskCoreException {
706 CaseDbConnection connection = null;
707 Statement statement = null;
708 ResultSet rs = null;
710 try {
711 connection = caseDb.getConnection();
712 statement = connection.createStatement();
713 rs = connection.executeQuery(statement, "SELECT attrs.id as id, attrs.obj_id AS obj_id, "
714 + "attrs.attribute_type_id AS attribute_type_id, "
715 + "attrs.value_type AS value_type, attrs.value_byte AS value_byte, "
716 + "attrs.value_text AS value_text, attrs.value_int32 AS value_int32, "
717 + "attrs.value_int64 AS value_int64, attrs.value_double AS value_double, "
718 + "types.type_name AS type_name, types.display_name AS display_name "
719 + "FROM tsk_file_attributes AS attrs "
720 + " INNER JOIN blackboard_attribute_types AS types "
721 + " ON attrs.attribute_type_id = types.attribute_type_id "
722 + " WHERE attrs.obj_id = " + file.getId());
723
724 ArrayList<Attribute> attributes = new ArrayList<Attribute>();
725 while (rs.next()) {
726 int attributeTypeId = rs.getInt("attribute_type_id");
727 String attributeTypeName = rs.getString("type_name");
728 BlackboardAttribute.Type attributeType;
729 if (this.typeIdToAttributeTypeMap.containsKey(attributeTypeId)) {
730 attributeType = this.typeIdToAttributeTypeMap.get(attributeTypeId);
731 } else {
732 attributeType = new BlackboardAttribute.Type(attributeTypeId, attributeTypeName,
733 rs.getString("display_name"),
734 BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getInt("value_type")));
735 this.typeIdToAttributeTypeMap.put(attributeTypeId, attributeType);
736 this.typeNameToAttributeTypeMap.put(attributeTypeName, attributeType);
737 }
738
739 final Attribute attr = new Attribute(
740 rs.getLong("id"),
741 rs.getLong("obj_id"),
742 attributeType,
743 rs.getInt("value_int32"),
744 rs.getLong("value_int64"),
745 rs.getDouble("value_double"),
746 rs.getString("value_text"),
747 rs.getBytes("value_byte"), caseDb
748 );
749 attributes.add(attr);
750 }
751 return attributes;
752 } catch (SQLException ex) {
753 throw new TskCoreException("Error getting attributes for file, file id = " + file.getId(), ex);
754 } finally {
755 closeResultSet(rs);
756 closeStatement(statement);
757 closeConnection(connection);
758 caseDb.releaseSingleUserCaseReadLock();
759 }
760 }
761
771 void initBlackboardArtifactTypes(CaseDbConnection connection) throws SQLException {
772 caseDb.acquireSingleUserCaseWriteLock();
773 try {
774 try (Statement getExistingStatement = connection.createStatement()) {
775 /*
776 * Determine which types, if any, have already been added to the
777 * case database, and load them into the type caches. For a case
778 * that is being reopened, this should reduce the number of
779 * separate INSERT staements that will be executed below.
780 */
781 ResultSet resultSet = connection.executeQuery(getExistingStatement, "SELECT artifact_type_id, type_name, display_name, category_type FROM blackboard_artifact_types"); //NON-NLS
782 while (resultSet.next()) {
783 BlackboardArtifact.Type type = new BlackboardArtifact.Type(resultSet.getInt("artifact_type_id"),
784 resultSet.getString("type_name"), resultSet.getString("display_name"),
785 BlackboardArtifact.Category.fromID(resultSet.getInt("category_type")));
786 typeIdToArtifactTypeMap.put(type.getTypeID(), type);
787 typeNameToArtifactTypeMap.put(type.getTypeName(), type);
788 }
789 }
790
791 /*
792 * INSERT any missing standard types. A conflict clause is used to
793 * avoid a potential race condition. It also eliminates the need to
794 * add schema update code when new types are added.
795 *
796 * The use here of the soon to be deprecated
797 * BlackboardArtifact.ARTIFACT_TYPE enum instead of the
798 * BlackboardArtifact.Type.STANDARD_TYPES collection currently
799 * ensures that the deprecated types in the former, and not in the
800 * latter, are added to the case database.
801 */
802 String insertSql;
803 if (caseDb.getDatabaseType() == TskData.DbType.POSTGRESQL) {
804 insertSql = "INSERT INTO blackboard_artifact_types (artifact_type_id, type_name, display_name, category_type) VALUES (?, ?, ?, ?) ON CONFLICT DO NOTHING"; //NON-NLS
805 } else {
806 insertSql = "INSERT OR IGNORE INTO blackboard_artifact_types (artifact_type_id, type_name, display_name, category_type) VALUES (?, ?, ?, ?)"; //NON-NLS
807 }
808
809 for (BlackboardArtifact.ARTIFACT_TYPE type : BlackboardArtifact.ARTIFACT_TYPE.values()) {
810 if (typeIdToArtifactTypeMap.containsKey(type.getTypeID())) {
811 continue;
812 }
813 try (PreparedStatement insertArtType = connection.getPreparedStatement(insertSql, Statement.RETURN_GENERATED_KEYS)) {
814 insertArtType.setInt(1, type.getTypeID());
815 insertArtType.setString(2, type.getLabel());
816 insertArtType.setString(3, type.getDisplayName());
817 insertArtType.setInt(4, type.getCategory().getID());
818 insertArtType.executeUpdate();
819 }
820 typeIdToArtifactTypeMap.put(type.getTypeID(), new BlackboardArtifact.Type(type));
821 typeNameToArtifactTypeMap.put(type.getLabel(), new BlackboardArtifact.Type(type));
822 }
823 if (caseDb.getDatabaseType() == TskData.DbType.POSTGRESQL) {
824 int newPrimaryKeyIndex = Collections.max(Arrays.asList(BlackboardArtifact.ARTIFACT_TYPE.values())).getTypeID() + 1;
825 try (Statement updateSequenceStatement = connection.createStatement()) {
826 updateSequenceStatement.execute("ALTER SEQUENCE blackboard_artifact_types_artifact_type_id_seq RESTART WITH " + newPrimaryKeyIndex); //NON-NLS
827 }
828 }
829 } finally {
830 caseDb.releaseSingleUserCaseWriteLock();
831 }
832 }
833
843 void initBlackboardAttributeTypes(CaseDbConnection connection) throws SQLException {
844 caseDb.acquireSingleUserCaseWriteLock();
845 try (Statement statement = connection.createStatement()) {
846 /*
847 * Determine which types, if any, have already been added to the
848 * case database, and load them into the type caches. For a case
849 * that is being reopened, this should reduce the number of separate
850 * INSERT staements that will be executed below.
851 */
852 ResultSet resultSet = connection.executeQuery(statement, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types"); //NON-NLS
853 while (resultSet.next()) {
854 BlackboardAttribute.Type type = new BlackboardAttribute.Type(resultSet.getInt("attribute_type_id"),
855 resultSet.getString("type_name"), resultSet.getString("display_name"),
856 BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(resultSet.getLong("value_type")));
857 typeIdToAttributeTypeMap.put(type.getTypeID(), type);
858 typeNameToAttributeTypeMap.put(type.getTypeName(), type);
859 }
860
861 /*
862 * INSERT any missing standard types. A conflict clause is used to
863 * avoid a potential race condition. It also eliminates the need to
864 * add schema update code when new types are added.
865 *
866 * The use here of the soon to be deprecated
867 * BlackboardAttribute.ATTRIBUTE_TYPE enum instead of the
868 * BlackboardAttribute.Type.STANDARD_TYPES collection currently
869 * ensures that the deprecated types in the former, and not in the
870 * latter, are added to the case database.
871 */
872 for (BlackboardAttribute.ATTRIBUTE_TYPE type : BlackboardAttribute.ATTRIBUTE_TYPE.values()) {
873 if (typeIdToAttributeTypeMap.containsKey(type.getTypeID())) {
874 continue;
875 }
876 if (caseDb.getDatabaseType() == TskData.DbType.POSTGRESQL) {
877 statement.execute("INSERT INTO blackboard_attribute_types (attribute_type_id, type_name, display_name, value_type) VALUES (" + type.getTypeID() + ", '" + type.getLabel() + "', '" + type.getDisplayName() + "', '" + type.getValueType().getType() + "') ON CONFLICT DO NOTHING"); //NON-NLS
878 } else {
879 statement.execute("INSERT OR IGNORE INTO blackboard_attribute_types (attribute_type_id, type_name, display_name, value_type) VALUES (" + type.getTypeID() + ", '" + type.getLabel() + "', '" + type.getDisplayName() + "', '" + type.getValueType().getType() + "')"); //NON-NLS
880 }
881 typeIdToAttributeTypeMap.put(type.getTypeID(), new BlackboardAttribute.Type(type));
882 typeNameToAttributeTypeMap.put(type.getLabel(), new BlackboardAttribute.Type(type));
883 }
884 if (caseDb.getDatabaseType() == TskData.DbType.POSTGRESQL) {
885 int newPrimaryKeyIndex = Collections.max(Arrays.asList(BlackboardAttribute.ATTRIBUTE_TYPE.values())).getTypeID() + 1;
886 statement.execute("ALTER SEQUENCE blackboard_attribute_types_attribute_type_id_seq RESTART WITH " + newPrimaryKeyIndex); //NON-NLS
887 }
888 } finally {
889 caseDb.releaseSingleUserCaseWriteLock();
890 }
891 }
892
915 public AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, long objId, Long dataSourceObjId, Score score,
916 String conclusion, String configuration, String justification, Collection<BlackboardAttribute> attributesList)
918
919 if (artifactType.getCategory() != BlackboardArtifact.Category.ANALYSIS_RESULT) {
920 throw new BlackboardException(String.format("Artifact type (name = %s) is not of Analysis Result category. ", artifactType.getTypeName()));
921 }
922
923 CaseDbTransaction transaction = caseDb.beginTransaction();
924 try {
925 AnalysisResultAdded analysisResult = newAnalysisResult(artifactType, objId, dataSourceObjId, score,
926 conclusion, configuration, justification, attributesList, transaction);
927 transaction.commit();
928 return analysisResult;
930 try {
931 transaction.rollback();
932 } catch (TskCoreException ex2) {
933 LOGGER.log(Level.SEVERE, "Failed to rollback transaction after exception. "
934 + "Error invoking newAnalysisResult with dataSourceObjId: "
935 + (dataSourceObjId == null ? "<null>" : dataSourceObjId)
936 + ", sourceObjId: " + objId, ex2);
937 }
938 throw ex;
939 }
940 }
941
964 public AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, long objId, Long dataSourceObjId, Score score,
965 String conclusion, String configuration, String justification, Collection<BlackboardAttribute> attributesList, CaseDbTransaction transaction) throws BlackboardException {
966
967 if (artifactType.getCategory() != BlackboardArtifact.Category.ANALYSIS_RESULT) {
968 throw new BlackboardException(String.format("Artifact type (name = %s) is not of Analysis Result category. ", artifactType.getTypeName()));
969 }
970
971 try {
972 // add analysis result
973 AnalysisResult analysisResult = caseDb.newAnalysisResult(artifactType, objId, dataSourceObjId, score, conclusion, configuration, justification, transaction.getConnection());
974
975 // add the given attributes
976 if (attributesList != null && !attributesList.isEmpty()) {
977 analysisResult.addAttributes(attributesList, transaction);
978 }
979
980 // update the final score for the object
981 Score aggregateScore = caseDb.getScoringManager().updateAggregateScoreAfterAddition(objId, dataSourceObjId, analysisResult.getScore(), transaction);
982
983 // return the analysis result and the current aggregate score.
984 return new AnalysisResultAdded(analysisResult, aggregateScore);
985
986 } catch (TskCoreException ex) {
987 throw new BlackboardException("Failed to add analysis result.", ex);
988 }
989 }
990
1006
1007 CaseDbTransaction transaction = this.caseDb.beginTransaction();
1008 try {
1009 Score score = deleteAnalysisResult(analysisResult, transaction);
1010 transaction.commit();
1011 transaction = null;
1012
1013 return score;
1014 } finally {
1015 if (transaction != null) {
1016 transaction.rollback();
1017 }
1018 }
1019 }
1020
1034 public Score deleteAnalysisResult(long artifactObjId, CaseDbTransaction transaction) throws TskCoreException {
1035
1036 List<AnalysisResult> analysisResults = getAnalysisResultsWhere(" artifacts.artifact_obj_id = " + artifactObjId, transaction.getConnection());
1037
1038 if (analysisResults.isEmpty()) {
1039 throw new TskCoreException(String.format("Analysis Result not found for artifact obj id %d", artifactObjId));
1040 }
1041
1042 return deleteAnalysisResult(analysisResults.get(0), transaction);
1043 }
1044
1058 private Score deleteAnalysisResult(AnalysisResult analysisResult, CaseDbTransaction transaction) throws TskCoreException {
1059
1060 try {
1061 CaseDbConnection connection = transaction.getConnection();
1062
1063 // delete the blackboard artifacts row. This will also delete the tsk_analysis_result row
1064 String deleteSQL = "DELETE FROM blackboard_artifacts WHERE artifact_obj_id = ?";
1065
1066 PreparedStatement deleteStatement = connection.getPreparedStatement(deleteSQL, Statement.RETURN_GENERATED_KEYS);
1067 deleteStatement.clearParameters();
1068 deleteStatement.setLong(1, analysisResult.getId());
1069
1070 deleteStatement.executeUpdate();
1071
1072 // register the deleted result with the transaction so an event can be fired for it.
1073 transaction.registerDeletedAnalysisResult(analysisResult.getObjectID());
1074
1075 return caseDb.getScoringManager().updateAggregateScoreAfterDeletion(analysisResult.getObjectID(), analysisResult.getDataSourceObjectID(), transaction);
1076
1077 } catch (SQLException ex) {
1078 throw new TskCoreException(String.format("Error deleting analysis result with artifact obj id %d", analysisResult.getId()), ex);
1079 }
1080 }
1081
1097 public Score ignoreAnalysisResultScore(AnalysisResult analysisResult, boolean ignore) throws TskCoreException {
1098
1099 CaseDbTransaction transaction = this.caseDb.beginTransaction();
1100 try {
1101 Score score = ignoreAnalysisResultScore(analysisResult, ignore, transaction);
1102 transaction.commit();
1103 transaction = null;
1104
1105 return score;
1106 } finally {
1107 if (transaction != null) {
1108 transaction.rollback();
1109 }
1110 }
1111 }
1112
1129 public Score ignoreAnalysisResultScore(long artifactObjId, boolean ignore, CaseDbTransaction transaction) throws TskCoreException {
1130
1131 List<AnalysisResult> analysisResults = getAnalysisResultsWhere(" artifacts.artifact_obj_id = " + artifactObjId, transaction.getConnection());
1132
1133 if (analysisResults.isEmpty()) {
1134 throw new TskCoreException(String.format("Analysis Result not found for artifact obj id %d", artifactObjId));
1135 }
1136
1137 return ignoreAnalysisResultScore(analysisResults.get(0), ignore, transaction);
1138 }
1139
1156 private Score ignoreAnalysisResultScore(AnalysisResult analysisResult, boolean ignore, CaseDbTransaction transaction) throws TskCoreException {
1157
1158 try {
1159 CaseDbConnection connection = transaction.getConnection();
1160
1161 String query = "UPDATE tsk_analysis_results SET ignore_score = CASE WHEN ? THEN 1 ELSE 0 END WHERE artifact_obj_id = ?";
1162
1163 PreparedStatement ignoreScoreStatement = connection.getPreparedStatement(query, Statement.RETURN_GENERATED_KEYS);
1164 ignoreScoreStatement.clearParameters();
1165 ignoreScoreStatement.setBoolean(1, ignore);
1166 ignoreScoreStatement.setLong(2, analysisResult.getId());
1167
1168 ignoreScoreStatement.executeUpdate();
1169
1170 // recalculate the score from scratch and send a score change event if the score has changed
1171 return caseDb.getScoringManager().updateAggregateScoreAfterDeletion(analysisResult.getObjectID(), analysisResult.getDataSourceObjectID(), transaction);
1172
1173 } catch (SQLException ex) {
1174 throw new TskCoreException(String.format("Error ignoring score of analysis result with artifact obj id %d", analysisResult.getId()), ex);
1175 }
1176 }
1177
1178 private final static String ANALYSIS_RESULT_QUERY_STRING_GENERIC = "SELECT DISTINCT artifacts.artifact_id AS artifact_id, " //NON-NLS
1179 + " artifacts.obj_id AS obj_id, artifacts.artifact_obj_id AS artifact_obj_id, artifacts.data_source_obj_id AS data_source_obj_id, artifacts.artifact_type_id AS artifact_type_id, "
1180 + " types.type_name AS type_name, types.display_name AS display_name, types.category_type as category_type,"//NON-NLS
1181 + " artifacts.review_status_id AS review_status_id, " //NON-NLS
1182 + " results.conclusion AS conclusion, results.significance AS significance, results.priority AS priority, "
1183 + " results.configuration AS configuration, results.justification AS justification, "
1184 + " results.ignore_score AS ignore_score "
1185 + " FROM blackboard_artifacts AS artifacts "
1186 + " JOIN blackboard_artifact_types AS types " //NON-NLS
1187 + " ON artifacts.artifact_type_id = types.artifact_type_id" //NON-NLS
1188 + " LEFT JOIN tsk_analysis_results AS results "
1189 + " ON artifacts.artifact_obj_id = results.artifact_obj_id "; //NON-NLS
1190
1191 private final static String ANALYSIS_RESULT_QUERY_STRING_WITH_ATTRIBUTES
1192 = ANALYSIS_RESULT_QUERY_STRING_GENERIC
1193 + " JOIN blackboard_attributes AS attributes " //NON-NLS
1194 + " ON artifacts.artifact_id = attributes.artifact_id " //NON-NLS
1195 + " WHERE types.category_type = " + BlackboardArtifact.Category.ANALYSIS_RESULT.getID(); // NON-NLS
1196
1197 private final static String ANALYSIS_RESULT_QUERY_STRING_WHERE
1198 = ANALYSIS_RESULT_QUERY_STRING_GENERIC
1199 + " WHERE artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() //NON-NLS
1200 + " AND types.category_type = " + BlackboardArtifact.Category.ANALYSIS_RESULT.getID(); // NON-NLS
1201
1212 public List<AnalysisResult> getAnalysisResultsByType(int artifactTypeId) throws TskCoreException {
1213 return getAnalysisResultsWhere(" artifacts.artifact_type_id = " + artifactTypeId);
1214 }
1215
1227 public List<AnalysisResult> getAnalysisResultsByType(int artifactTypeId, long dataSourceObjId) throws TskCoreException {
1228 return getAnalysisResultsWhere(" artifacts.artifact_type_id = " + artifactTypeId + " AND artifacts.data_source_obj_id = " + dataSourceObjId);
1229 }
1230
1244 public List<AnalysisResult> getAnalysisResults(long dataSourceObjId, Integer artifactTypeID) throws TskCoreException {
1245 caseDb.acquireSingleUserCaseReadLock();
1246 try (CaseDbConnection connection = caseDb.getConnection()) {
1247 String whereClause = " artifacts.data_source_obj_id = " + dataSourceObjId;
1248 if (artifactTypeID != null) {
1249 whereClause += " AND artifacts.artifact_type_id = " + artifactTypeID;
1250 }
1251 return getAnalysisResultsWhere(whereClause, connection);
1252 } finally {
1253 caseDb.releaseSingleUserCaseReadLock();
1254 }
1255 }
1256
1267 public List<AnalysisResult> getAnalysisResults(long sourceObjId) throws TskCoreException {
1268 return getAnalysisResultsWhere(" artifacts.obj_id = " + sourceObjId);
1269 }
1270
1281 List<DataArtifact> getDataArtifactsBySource(long sourceObjId) throws TskCoreException {
1283 try (CaseDbConnection connection = caseDb.getConnection()) {
1284 return getDataArtifactsWhere(String.format(" artifacts.obj_id = %d", sourceObjId), connection);
1285 } finally {
1287 }
1288 }
1289
1299 public boolean hasDataArtifacts(long sourceObjId) throws TskCoreException {
1300 return hasArtifactsOfCategory(BlackboardArtifact.Category.DATA_ARTIFACT, sourceObjId);
1301 }
1302
1313 public boolean hasAnalysisResults(long sourceObjId) throws TskCoreException {
1314 return hasArtifactsOfCategory(BlackboardArtifact.Category.ANALYSIS_RESULT, sourceObjId);
1315 }
1316
1329 private boolean hasArtifactsOfCategory(BlackboardArtifact.Category category, long sourceObjId) throws TskCoreException {
1330 String queryString = "SELECT COUNT(*) AS count " //NON-NLS
1331 + " FROM blackboard_artifacts AS arts "
1332 + " JOIN blackboard_artifact_types AS types " //NON-NLS
1333 + " ON arts.artifact_type_id = types.artifact_type_id" //NON-NLS
1334 + " WHERE types.category_type = " + category.getID()
1335 + " AND arts.obj_id = " + sourceObjId;
1336
1338 try (SleuthkitCase.CaseDbConnection connection = caseDb.getConnection();
1339 Statement statement = connection.createStatement();
1340 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
1341 if (resultSet.next()) {
1342 return resultSet.getLong("count") > 0;
1343 }
1344 return false;
1345 } catch (SQLException ex) {
1346 throw new TskCoreException("Error getting artifact types is use for data source." + ex.getMessage(), ex);
1347 } finally {
1349 }
1350 }
1351
1364 List<AnalysisResult> getAnalysisResults(long sourceObjId, CaseDbConnection connection) throws TskCoreException {
1365 return getAnalysisResultsWhere(" artifacts.obj_id = " + sourceObjId, connection);
1366 }
1367
1379 public List<AnalysisResult> getAnalysisResults(long sourceObjId, int artifactTypeId) throws TskCoreException {
1380 // Get the artifact type to check that it in the analysis result category.
1381 BlackboardArtifact.Type artifactType = getArtifactType(artifactTypeId);
1382 if (artifactType.getCategory() != BlackboardArtifact.Category.ANALYSIS_RESULT) {
1383 throw new TskCoreException(String.format("Artifact type id %d is not in analysis result catgeory.", artifactTypeId));
1384 }
1385
1386 String whereClause = " types.artifact_type_id = " + artifactTypeId
1387 + " AND artifacts.obj_id = " + sourceObjId;
1388 return getAnalysisResultsWhere(whereClause);
1389 }
1390
1402 public List<AnalysisResult> getAnalysisResultsWhere(String whereClause) throws TskCoreException {
1403 caseDb.acquireSingleUserCaseReadLock();
1404 try (CaseDbConnection connection = caseDb.getConnection()) {
1405 return getAnalysisResultsWhere(whereClause, connection);
1406 } finally {
1407 caseDb.releaseSingleUserCaseReadLock();
1408 }
1409 }
1410
1423 List<AnalysisResult> getAnalysisResultsWhere(String whereClause, CaseDbConnection connection) throws TskCoreException {
1424
1425 final String queryString = ANALYSIS_RESULT_QUERY_STRING_WHERE
1426 + " AND " + whereClause;
1427
1428 try (Statement statement = connection.createStatement();
1429 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
1430
1431 List<AnalysisResult> analysisResults = resultSetToAnalysisResults(resultSet);
1432 return analysisResults;
1433 } catch (SQLException ex) {
1434 throw new TskCoreException(String.format("Error getting analysis results for WHERE clause = '%s'", whereClause), ex);
1435 }
1436 }
1437
1447 public AnalysisResult getAnalysisResultById(long artifactObjId) throws TskCoreException {
1448
1449 String whereClause = " artifacts.artifact_obj_id = " + artifactObjId;
1450 List<AnalysisResult> results = getAnalysisResultsWhere(whereClause);
1451
1452 if (results.isEmpty()) { // throw an error if no analysis result found by id.
1453 throw new TskCoreException(String.format("Error getting analysis result with id = '%d'", artifactObjId));
1454 }
1455 if (results.size() > 1) { // should not happen - throw an error
1456 throw new TskCoreException(String.format("Multiple analysis results found with id = '%d'", artifactObjId));
1457 }
1458
1459 return results.get(0);
1460 }
1461
1477 private List<AnalysisResult> resultSetToAnalysisResults(ResultSet resultSet) throws SQLException, TskCoreException {
1478 ArrayList<AnalysisResult> analysisResults = new ArrayList<>();
1479
1480 while (resultSet.next()) {
1481 analysisResults.add(new AnalysisResult(caseDb, resultSet.getLong("artifact_id"), resultSet.getLong("obj_id"),
1482 resultSet.getLong("artifact_obj_id"),
1483 resultSet.getObject("data_source_obj_id") != null ? resultSet.getLong("data_source_obj_id") : null,
1484 resultSet.getInt("artifact_type_id"), resultSet.getString("type_name"), resultSet.getString("display_name"),
1485 BlackboardArtifact.ReviewStatus.withID(resultSet.getInt("review_status_id")),
1486 new Score(Score.Significance.fromID(resultSet.getInt("significance")), Score.Priority.fromID(resultSet.getInt("priority"))),
1487 resultSet.getString("conclusion"), resultSet.getString("configuration"), resultSet.getString("justification"),
1488 resultSet.getBoolean("ignore_score")));
1489 } //end for each resultSet
1490
1491 return analysisResults;
1492 }
1493
1494 private final static String DATA_ARTIFACT_QUERY_STRING_GENERIC = "SELECT DISTINCT artifacts.artifact_id AS artifact_id, " //NON-NLS
1495 + "artifacts.obj_id AS obj_id, artifacts.artifact_obj_id AS artifact_obj_id, artifacts.data_source_obj_id AS data_source_obj_id, artifacts.artifact_type_id AS artifact_type_id, " //NON-NLS
1496 + " types.type_name AS type_name, types.display_name AS display_name, types.category_type as category_type,"//NON-NLS
1497 + " artifacts.review_status_id AS review_status_id, " //NON-NLS
1498 + " data_artifacts.os_account_obj_id as os_account_obj_id " //NON-NLS
1499 + " FROM blackboard_artifacts AS artifacts " //NON-NLS
1500 + " JOIN blackboard_artifact_types AS types " //NON-NLS
1501 + " ON artifacts.artifact_type_id = types.artifact_type_id" //NON-NLS
1502 + " LEFT JOIN tsk_data_artifacts AS data_artifacts " //NON-NLS
1503 + " ON artifacts.artifact_obj_id = data_artifacts.artifact_obj_id "; //NON-NLS
1504
1505 private final static String DATA_ARTIFACT_QUERY_STRING_WITH_ATTRIBUTES
1506 = DATA_ARTIFACT_QUERY_STRING_GENERIC
1507 + " JOIN blackboard_attributes AS attributes " //NON-NLS
1508 + " ON artifacts.artifact_id = attributes.artifact_id " //NON-NLS
1509 + " WHERE types.category_type = " + BlackboardArtifact.Category.DATA_ARTIFACT.getID(); // NON-NLS
1510
1511 private final static String DATA_ARTIFACT_QUERY_STRING_WHERE
1512 = DATA_ARTIFACT_QUERY_STRING_GENERIC
1513 + " WHERE artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() //NON-NLS
1514 + " AND types.category_type = " + BlackboardArtifact.Category.DATA_ARTIFACT.getID(); // NON-NLS
1515
1528 public List<DataArtifact> getDataArtifacts(long dataSourceObjId, Integer artifactTypeID) throws TskCoreException {
1529 caseDb.acquireSingleUserCaseReadLock();
1530 try (CaseDbConnection connection = caseDb.getConnection()) {
1531 String whereClause = " artifacts.data_source_obj_id = " + dataSourceObjId;
1532 if (artifactTypeID != null) {
1533 whereClause += " AND artifacts.artifact_type_id = " + artifactTypeID;
1534 }
1535 return getDataArtifactsWhere(whereClause, connection);
1536 } finally {
1537 caseDb.releaseSingleUserCaseReadLock();
1538 }
1539 }
1540
1552 public List<DataArtifact> getDataArtifacts(int artifactTypeID, long dataSourceObjId) throws TskCoreException {
1553
1554 // Get the artifact type to check that it in the data artifact category.
1555 BlackboardArtifact.Type artifactType = getArtifactType(artifactTypeID);
1556 if (artifactType.getCategory() != BlackboardArtifact.Category.DATA_ARTIFACT) {
1557 throw new TskCoreException(String.format("Artifact type id %d is not in data artifact catgeory.", artifactTypeID));
1558 }
1559
1560 caseDb.acquireSingleUserCaseReadLock();
1561 try (CaseDbConnection connection = caseDb.getConnection()) {
1562 String whereClause = "artifacts.data_source_obj_id = " + dataSourceObjId
1563 + " AND artifacts.artifact_type_id = " + artifactTypeID;
1564
1565 return getDataArtifactsWhere(whereClause, connection);
1566 } finally {
1567 caseDb.releaseSingleUserCaseReadLock();
1568 }
1569 }
1570
1581 public List<DataArtifact> getDataArtifacts(int artifactTypeID) throws TskCoreException {
1582 // Get the artifact type to check that it in the data artifact category.
1583 BlackboardArtifact.Type artifactType = getArtifactType(artifactTypeID);
1584 if (artifactType.getCategory() != BlackboardArtifact.Category.DATA_ARTIFACT) {
1585 throw new TskCoreException(String.format("Artifact type id %d is not in data artifact catgeory.", artifactTypeID));
1586 }
1587
1588 caseDb.acquireSingleUserCaseReadLock();
1589 try (CaseDbConnection connection = caseDb.getConnection()) {
1590 String whereClause = " artifacts.artifact_type_id = " + artifactTypeID;
1591
1592 return getDataArtifactsWhere(whereClause, connection);
1593 } finally {
1594 caseDb.releaseSingleUserCaseReadLock();
1595 }
1596 }
1597
1608 public DataArtifact getDataArtifactById(long artifactObjId) throws TskCoreException {
1609 caseDb.acquireSingleUserCaseReadLock();
1610 try (CaseDbConnection connection = caseDb.getConnection()) {
1611 String whereClause = " artifacts.artifact_obj_id = " + artifactObjId;
1612
1613 List<DataArtifact> artifacts = getDataArtifactsWhere(whereClause, connection);
1614 if (artifacts.isEmpty()) { // throw an error if no analysis result found by id.
1615 throw new TskCoreException(String.format("Error getting data artifact with id = '%d'", artifactObjId));
1616 }
1617 if (artifacts.size() > 1) { // should not happen - throw an error
1618 throw new TskCoreException(String.format("Multiple data artifacts found with id = '%d'", artifactObjId));
1619 }
1620
1621 return artifacts.get(0);
1622 } finally {
1623 caseDb.releaseSingleUserCaseReadLock();
1624 }
1625 }
1626
1637 public List<DataArtifact> getDataArtifactsWhere(String whereClause) throws TskCoreException {
1638 caseDb.acquireSingleUserCaseReadLock();
1639 try (CaseDbConnection connection = caseDb.getConnection()) {
1640 return getDataArtifactsWhere(whereClause, connection);
1641 } finally {
1642 caseDb.releaseSingleUserCaseReadLock();
1643 }
1644 }
1645
1658 List<DataArtifact> getDataArtifactsWhere(String whereClause, CaseDbConnection connection) throws TskCoreException {
1659
1660 final String queryString = DATA_ARTIFACT_QUERY_STRING_WHERE
1661 + " AND " + whereClause + " ";
1662
1663 try (Statement statement = connection.createStatement();
1664 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
1665
1666 List<DataArtifact> dataArtifacts = resultSetToDataArtifacts(resultSet);
1667 return dataArtifacts;
1668 } catch (SQLException ex) {
1669 throw new TskCoreException(String.format("Error getting data artifacts with queryString = %s", queryString), ex);
1670 }
1671 }
1672
1688 private List<DataArtifact> resultSetToDataArtifacts(ResultSet resultSet) throws SQLException, TskCoreException {
1689 ArrayList<DataArtifact> dataArtifacts = new ArrayList<>();
1690
1691 while (resultSet.next()) {
1692
1693 Long osAccountObjId = resultSet.getLong("os_account_obj_id");
1694 if (resultSet.wasNull()) {
1695 osAccountObjId = null;
1696 }
1697
1698 dataArtifacts.add(new DataArtifact(caseDb, resultSet.getLong("artifact_id"), resultSet.getLong("obj_id"),
1699 resultSet.getLong("artifact_obj_id"),
1700 resultSet.getObject("data_source_obj_id") != null ? resultSet.getLong("data_source_obj_id") : null,
1701 resultSet.getInt("artifact_type_id"), resultSet.getString("type_name"), resultSet.getString("display_name"),
1702 BlackboardArtifact.ReviewStatus.withID(resultSet.getInt("review_status_id")), osAccountObjId, false));
1703 } //end for each resultSet
1704
1705 return dataArtifacts;
1706 }
1707
1725 public synchronized BlackboardAttribute.Type getOrAddAttributeType(String typeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName) throws BlackboardException {
1726 // check local cache
1727 if (typeNameToAttributeTypeMap.containsKey(typeName)) {
1728 return typeNameToAttributeTypeMap.get(typeName);
1729 }
1730
1731 CaseDbTransaction trans = null;
1732 try {
1733 trans = this.caseDb.beginTransaction();
1734 String matchingAttrQuery = "SELECT attribute_type_id, type_name, display_name, value_type "
1735 + "FROM blackboard_attribute_types WHERE type_name = ?";
1736 // find matching attribute name
1737 PreparedStatement query = trans.getConnection().getPreparedStatement(matchingAttrQuery, Statement.RETURN_GENERATED_KEYS);
1738 query.clearParameters();
1739 query.setString(1, typeName);
1740 try (ResultSet rs = query.executeQuery()) {
1741 // if previously existing, commit the results and return the attribute type
1742 if (rs.next()) {
1743 trans.commit();
1744 trans = null;
1746 rs.getInt("attribute_type_id"),
1747 rs.getString("type_name"),
1748 rs.getString("display_name"),
1750 );
1751
1752 this.typeIdToAttributeTypeMap.put(foundType.getTypeID(), foundType);
1753 this.typeNameToAttributeTypeMap.put(foundType.getTypeName(), foundType);
1754
1755 return foundType;
1756 }
1757 }
1758
1759 // if not found in database, insert
1760 String insertStatement = "INSERT INTO blackboard_attribute_types (attribute_type_id, type_name, display_name, value_type) VALUES (\n"
1761 // get the maximum of the attribute type id's or the min user defined type id and add 1 to it for the new id
1762 + "(SELECT MAX(q.attribute_type_id) FROM (SELECT attribute_type_id FROM blackboard_attribute_types UNION SELECT " + (MIN_USER_DEFINED_TYPE_ID - 1) + ") q) + 1,\n"
1763 // typeName, displayName, valueType
1764 + "?, ?, ?)";
1765
1766 PreparedStatement insertPreparedStatement = trans.getConnection().getPreparedStatement(insertStatement, Statement.RETURN_GENERATED_KEYS);
1767 insertPreparedStatement.clearParameters();
1768 insertPreparedStatement.setString(1, typeName);
1769 insertPreparedStatement.setString(2, displayName);
1770 insertPreparedStatement.setLong(3, valueType.getType());
1771
1772 int numUpdated = insertPreparedStatement.executeUpdate();
1773
1774 // get id for inserted to create new attribute.
1775 Integer attrId = null;
1776
1777 if (numUpdated > 0) {
1778 try (ResultSet insertResult = insertPreparedStatement.getGeneratedKeys()) {
1779 if (insertResult.next()) {
1780 attrId = insertResult.getInt(1);
1781 }
1782 }
1783 }
1784
1785 if (attrId == null) {
1786 throw new BlackboardException(MessageFormat.format(
1787 "Error adding attribute type. Item with name {0} was not inserted successfully into the database.", typeName));
1788 }
1789
1790 trans.commit();
1791 trans = null;
1792
1793 BlackboardAttribute.Type type = new BlackboardAttribute.Type(attrId, typeName, displayName, valueType);
1794 this.typeIdToAttributeTypeMap.put(type.getTypeID(), type);
1795 this.typeNameToAttributeTypeMap.put(type.getTypeName(), type);
1796 return type;
1797 } catch (SQLException | TskCoreException ex) {
1798 throw new BlackboardException("Error adding attribute type: " + typeName, ex);
1799 } finally {
1800 try {
1801 if (trans != null) {
1802 trans.rollback();
1803 trans = null;
1804 }
1805 } catch (TskCoreException ex2) {
1806 LOGGER.log(Level.SEVERE, "Error rolling back transaction", ex2);
1807 }
1808 }
1809 }
1810
1822 public List<BlackboardArtifact.Type> getArtifactTypesInUse(long dataSourceObjId) throws TskCoreException {
1823
1824 final String queryString = "SELECT DISTINCT arts.artifact_type_id AS artifact_type_id, "
1825 + "types.type_name AS type_name, "
1826 + "types.display_name AS display_name, "
1827 + "types.category_type AS category_type "
1828 + "FROM blackboard_artifact_types AS types "
1829 + "INNER JOIN blackboard_artifacts AS arts "
1830 + "ON arts.artifact_type_id = types.artifact_type_id "
1831 + "WHERE arts.data_source_obj_id = " + dataSourceObjId;
1832
1833 caseDb.acquireSingleUserCaseReadLock();
1834 try (SleuthkitCase.CaseDbConnection connection = caseDb.getConnection();
1835 Statement statement = connection.createStatement();
1836 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
1837
1838 List<BlackboardArtifact.Type> uniqueArtifactTypes = new ArrayList<>();
1839 while (resultSet.next()) {
1840 uniqueArtifactTypes.add(new BlackboardArtifact.Type(resultSet.getInt("artifact_type_id"),
1841 resultSet.getString("type_name"), resultSet.getString("display_name"),
1842 BlackboardArtifact.Category.fromID(resultSet.getInt("category_type"))));
1843 }
1844 return uniqueArtifactTypes;
1845 } catch (SQLException ex) {
1846 throw new TskCoreException("Error getting artifact types is use for data source." + ex.getMessage(), ex);
1847 } finally {
1848 caseDb.releaseSingleUserCaseReadLock();
1849 }
1850 }
1851
1864 public long getArtifactsCount(int artifactTypeID, long dataSourceObjId) throws TskCoreException {
1865 return getArtifactsCountHelper(artifactTypeID,
1866 "blackboard_artifacts.data_source_obj_id = '" + dataSourceObjId + "';");
1867 }
1868
1880 public long getArtifactsCount(int artifactTypeID) throws TskCoreException {
1881 return getArtifactsCountHelper(artifactTypeID, null);
1882 }
1883
1896 public List<BlackboardArtifact> getArtifacts(int artifactTypeID, long dataSourceObjId) throws TskCoreException {
1897 String whereClause = String.format("artifacts.data_source_obj_id = %d", dataSourceObjId);
1898 return getArtifactsWhere(getArtifactType(artifactTypeID), whereClause);
1899 }
1900
1913 public List<BlackboardArtifact> getArtifacts(Collection<BlackboardArtifact.Type> artifactTypes,
1914 Collection<Long> dataSourceObjIds) throws TskCoreException {
1915
1916 if (artifactTypes.isEmpty() || dataSourceObjIds.isEmpty()) {
1917 return new ArrayList<>();
1918 }
1919
1920 String analysisResultQuery = "";
1921 String dataArtifactQuery = "";
1922
1923 for (BlackboardArtifact.Type type : artifactTypes) {
1924 if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
1925 if (!analysisResultQuery.isEmpty()) {
1926 analysisResultQuery += " OR ";
1927 }
1928 analysisResultQuery += "types.artifact_type_id = " + type.getTypeID();
1929 } else {
1930 if (!dataArtifactQuery.isEmpty()) {
1931 dataArtifactQuery += " OR ";
1932 }
1933 dataArtifactQuery += "types.artifact_type_id = " + type.getTypeID();
1934 }
1935 }
1936
1937 String dsQuery = "";
1938 for (long dsId : dataSourceObjIds) {
1939 if (!dsQuery.isEmpty()) {
1940 dsQuery += " OR ";
1941 }
1942 dsQuery += "artifacts.data_source_obj_id = " + dsId;
1943 }
1944
1945 List<BlackboardArtifact> artifacts = new ArrayList<>();
1946
1947 if (!analysisResultQuery.isEmpty()) {
1948 String fullQuery = "( " + analysisResultQuery + " ) AND (" + dsQuery + ") ";
1949 artifacts.addAll(this.getAnalysisResultsWhere(fullQuery));
1950 }
1951
1952 if (!dataArtifactQuery.isEmpty()) {
1953 String fullQuery = "( " + dataArtifactQuery + " ) AND (" + dsQuery + ") ";
1954 artifacts.addAll(this.getDataArtifactsWhere(fullQuery));
1955 }
1956
1957 return artifacts;
1958 }
1959
1976 public List<BlackboardArtifact> getArtifacts(BlackboardArtifact.Type artifactType,
1977 BlackboardAttribute.Type attributeType, String value, Long dataSourceObjId,
1978 boolean showRejected) throws TskCoreException {
1979
1980 String query = " AND artifacts.artifact_type_id = " + artifactType.getTypeID() //NON-NLS
1981 + " AND attributes.attribute_type_id = " + attributeType.getTypeID() //NON-NLS
1982 + ((value == null || value.isEmpty()) ? "" : " AND attributes.value_text = '" + value + "'") //NON-NLS
1983 + (showRejected ? "" : " AND artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID()) //NON-NLS
1984 + (dataSourceObjId != null ? " AND artifacts.data_source_obj_id = " + dataSourceObjId : ""); //NON-NLS
1985
1986 List<BlackboardArtifact> artifacts = new ArrayList<>();
1987 caseDb.acquireSingleUserCaseReadLock();
1988
1989 String finalQuery = (artifactType.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT
1990 ? ANALYSIS_RESULT_QUERY_STRING_WITH_ATTRIBUTES + query
1991 : DATA_ARTIFACT_QUERY_STRING_WITH_ATTRIBUTES + query);
1992
1993 try (CaseDbConnection connection = caseDb.getConnection()) {
1994 try (Statement statement = connection.createStatement();
1995 ResultSet resultSet = connection.executeQuery(statement, finalQuery);) {
1996
1997 if (artifactType.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
1998 artifacts.addAll(resultSetToAnalysisResults(resultSet));
1999 } else {
2000 artifacts.addAll(resultSetToDataArtifacts(resultSet));
2001 }
2002 } catch (SQLException ex) {
2003 throw new TskCoreException(String.format("Error getting results with queryString = '%s'", finalQuery), ex);
2004 }
2005 } finally {
2006 caseDb.releaseSingleUserCaseReadLock();
2007 }
2008 return artifacts;
2009 }
2010
2032 public List<BlackboardArtifact> getExactMatchKeywordSearchResults(String keyword, TskData.KeywordSearchQueryType searchType, String kwsListName, Long dataSourceId) throws TskCoreException {
2033 return getKeywordSearchResults(keyword, "", searchType, kwsListName, dataSourceId);
2034 }
2035
2061 public List<BlackboardArtifact> getKeywordSearchResults(String keyword, String regex, TskData.KeywordSearchQueryType searchType, String kwsListName, Long dataSourceId) throws TskCoreException {
2062
2063 String dataSourceClause = dataSourceId == null
2064 ? ""
2065 : " AND artifacts.data_source_obj_id = ? "; // dataSourceId
2066
2067 String kwsListClause = (kwsListName == null || kwsListName.isEmpty()
2068 ? " WHERE r.set_name IS NULL "
2069 : " WHERE r.set_name = ? ");
2070
2071 String keywordClause = (keyword == null || keyword.isEmpty()
2072 ? ""
2073 : " AND r.keyword = ? ");
2074
2075 String searchTypeClause = (searchType == null
2076 ? ""
2077 : " AND r.search_type = ? ");
2078
2079 String regexClause = (regex == null || regex.isEmpty()
2080 ? ""
2081 : " AND r.regexp_str = ? ");
2082
2083 String query = "SELECT r.* FROM ( "
2084 + " SELECT DISTINCT artifacts.artifact_id AS artifact_id, "
2085 + " artifacts.obj_id AS obj_id, "
2086 + " artifacts.artifact_obj_id AS artifact_obj_id, "
2087 + " artifacts.data_source_obj_id AS data_source_obj_id, "
2088 + " artifacts.artifact_type_id AS artifact_type_id, "
2089 + " types.type_name AS type_name, "
2090 + " types.display_name AS display_name, "
2091 + " types.category_type as category_type,"
2092 + " artifacts.review_status_id AS review_status_id, "
2093 + " results.conclusion AS conclusion, "
2094 + " results.significance AS significance, "
2095 + " results.priority AS priority, "
2096 + " results.configuration AS configuration, "
2097 + " results.justification AS justification, "
2098 + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = "
2099 + BlackboardAttribute.Type.TSK_SET_NAME.getTypeID() + " LIMIT 1) AS set_name, "
2100 + " (SELECT value_int32 FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = "
2101 + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID() + " LIMIT 1) AS search_type, "
2102 + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = "
2103 + BlackboardAttribute.Type.TSK_KEYWORD_REGEXP.getTypeID() + " LIMIT 1) AS regexp_str, "
2104 + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = "
2105 + BlackboardAttribute.Type.TSK_KEYWORD.getTypeID() + " LIMIT 1) AS keyword "
2106 + " FROM blackboard_artifacts artifacts "
2107 + " JOIN blackboard_artifact_types AS types "
2108 + " ON artifacts.artifact_type_id = types.artifact_type_id "
2109 + " LEFT JOIN tsk_analysis_results AS results "
2110 + " ON artifacts.artifact_obj_id = results.artifact_obj_id "
2111 + " WHERE types.category_type = " + BlackboardArtifact.Category.ANALYSIS_RESULT.getID()
2112 + " AND artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() + " "
2113 + dataSourceClause + " ) r "
2114 + kwsListClause
2115 + keywordClause
2116 + searchTypeClause
2117 + regexClause;
2118
2119 List<BlackboardArtifact> artifacts = new ArrayList<>();
2120 caseDb.acquireSingleUserCaseReadLock();
2121 try (CaseDbConnection connection = caseDb.getConnection()) {
2122
2123 try {
2124 PreparedStatement preparedStatement = connection.getPreparedStatement(query, Statement.RETURN_GENERATED_KEYS);
2125 preparedStatement.clearParameters();
2126 int paramIdx = 0;
2127 if (dataSourceId != null) {
2128 preparedStatement.setLong(++paramIdx, dataSourceId);
2129 }
2130
2131 if (!(kwsListName == null || kwsListName.isEmpty())) {
2132 preparedStatement.setString(++paramIdx, kwsListName);
2133 }
2134
2135 if (!(keyword == null || keyword.isEmpty())) {
2136 preparedStatement.setString(++paramIdx, keyword);
2137 }
2138
2139 if (searchType != null) {
2140 preparedStatement.setInt(++paramIdx, searchType.getType());
2141 }
2142
2143 if (!(regex == null || regex.isEmpty())) {
2144 preparedStatement.setString(++paramIdx, regex);
2145 }
2146
2147 try (ResultSet resultSet = connection.executeQuery(preparedStatement)) {
2148 artifacts.addAll(resultSetToAnalysisResults(resultSet));
2149 }
2150
2151 } catch (SQLException ex) {
2152 throw new TskCoreException(String.format("Error getting keyword search results with queryString = '%s'", query), ex);
2153 }
2154 } finally {
2155 caseDb.releaseSingleUserCaseReadLock();
2156 }
2157 return artifacts;
2158 }
2159
2173 private long getArtifactsCountHelper(int artifactTypeID, String whereClause) throws TskCoreException {
2174 String queryString = "SELECT COUNT(*) AS count FROM blackboard_artifacts "
2175 + "WHERE blackboard_artifacts.artifact_type_id = " + artifactTypeID
2176 + " AND blackboard_artifacts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID();
2177
2178 if (whereClause != null) {
2179 queryString += " AND " + whereClause;
2180 }
2181
2183 try (SleuthkitCase.CaseDbConnection connection = caseDb.getConnection();
2184 Statement statement = connection.createStatement();
2185 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
2186 long count = 0;
2187 if (resultSet.next()) {
2188 count = resultSet.getLong("count");
2189 }
2190 return count;
2191 } catch (SQLException ex) {
2192 throw new TskCoreException("Error getting artifact types is use for data source." + ex.getMessage(), ex);
2193 } finally {
2194 caseDb.releaseSingleUserCaseReadLock();
2195 }
2196 }
2197
2211 public boolean artifactExists(Content content, BlackboardArtifact.Type artifactType, Collection<BlackboardAttribute> attributes) throws TskCoreException {
2212 List<BlackboardArtifact> existingArtifacts = content.getArtifacts(artifactType.getTypeID());
2213 for (BlackboardArtifact artifact : existingArtifacts) {
2214 if (attributesMatch(artifact.getAttributes(), attributes)) {
2215 return true;
2216 }
2217 }
2218 return false;
2219 }
2220
2236 @Deprecated
2237 public boolean artifactExists(Content content, BlackboardArtifact.ARTIFACT_TYPE artifactType, Collection<BlackboardAttribute> attributes) throws TskCoreException {
2238 return artifactExists(content, getArtifactType(artifactType.getTypeID()), attributes);
2239 }
2240
2250 private boolean attributesMatch(Collection<BlackboardAttribute> fileAttributesList, Collection<BlackboardAttribute> expectedAttributesList) {
2251 for (BlackboardAttribute expectedAttribute : expectedAttributesList) {
2252 boolean match = false;
2253 for (BlackboardAttribute fileAttribute : fileAttributesList) {
2254 BlackboardAttribute.Type attributeType = fileAttribute.getAttributeType();
2255 if (attributeType.getTypeID() != expectedAttribute.getAttributeType().getTypeID()) {
2256 continue;
2257 }
2258
2259 Object fileAttributeValue;
2260 Object expectedAttributeValue;
2261 switch (attributeType.getValueType()) {
2262 case BYTE:
2263 fileAttributeValue = fileAttribute.getValueBytes();
2264 expectedAttributeValue = expectedAttribute.getValueBytes();
2265 break;
2266 case DOUBLE:
2267 fileAttributeValue = fileAttribute.getValueDouble();
2268 expectedAttributeValue = expectedAttribute.getValueDouble();
2269 break;
2270 case INTEGER:
2271 fileAttributeValue = fileAttribute.getValueInt();
2272 expectedAttributeValue = expectedAttribute.getValueInt();
2273 break;
2274 case LONG: // Fall-thru
2275 case DATETIME:
2276 fileAttributeValue = fileAttribute.getValueLong();
2277 expectedAttributeValue = expectedAttribute.getValueLong();
2278 break;
2279 case STRING: // Fall-thru
2280 case JSON:
2281 fileAttributeValue = fileAttribute.getValueString();
2282 expectedAttributeValue = expectedAttribute.getValueString();
2283 break;
2284 default:
2285 fileAttributeValue = fileAttribute.getDisplayString();
2286 expectedAttributeValue = expectedAttribute.getDisplayString();
2287 break;
2288 }
2289
2290 /*
2291 * If the exact attribute was found, mark it as a match to
2292 * continue looping through the expected attributes list.
2293 */
2294 if (fileAttributeValue instanceof byte[]) {
2295 if (Arrays.equals((byte[]) fileAttributeValue, (byte[]) expectedAttributeValue)) {
2296 match = true;
2297 break;
2298 }
2299 } else if (fileAttributeValue.equals(expectedAttributeValue)) {
2300 match = true;
2301 break;
2302 }
2303 }
2304 if (!match) {
2305 /*
2306 * The exact attribute type/value combination was not found.
2307 */
2308 return false;
2309 }
2310 }
2311
2312 /*
2313 * All attribute type/value combinations were found in the provided
2314 * attributes list.
2315 */
2316 return true;
2317
2318 }
2319
2323 public static final class BlackboardException extends Exception {
2324
2325 private static final long serialVersionUID = 1L;
2326
2332 BlackboardException(String message) {
2333 super(message);
2334 }
2335
2343 BlackboardException(String message, Throwable cause) {
2344 super(message, cause);
2345 }
2346 }
2347
2364 public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long sourceObjId, Long dataSourceObjId,
2365 Collection<BlackboardAttribute> attributes, Long osAccountId) throws TskCoreException {
2366
2367 if (artifactType.getCategory() != BlackboardArtifact.Category.DATA_ARTIFACT) {
2368 throw new TskCoreException(String.format("Artifact type (name = %s) is not of Data Artifact category. ", artifactType.getTypeName()));
2369 }
2370
2371 CaseDbTransaction transaction = caseDb.beginTransaction();
2372 try {
2373 DataArtifact dataArtifact = newDataArtifact(artifactType, sourceObjId, dataSourceObjId,
2374 attributes, osAccountId, transaction);
2375 transaction.commit();
2376 return dataArtifact;
2377 } catch (TskCoreException ex) {
2378 try {
2379 transaction.rollback();
2380 } catch (TskCoreException ex2) {
2381 LOGGER.log(Level.SEVERE, "Failed to rollback transaction after exception. "
2382 + "Error invoking newDataArtifact with dataSourceObjId: " + dataSourceObjId + ", sourceObjId: " + sourceObjId, ex2);
2383 }
2384 throw ex;
2385 }
2386 }
2387
2409 public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long sourceObjId, Long dataSourceObjId,
2410 Collection<BlackboardAttribute> attributes, Long osAccountObjId, final CaseDbTransaction transaction) throws TskCoreException {
2411
2412 return newDataArtifact(artifactType, sourceObjId, dataSourceObjId,
2413 attributes, osAccountObjId, OsAccountInstance.OsAccountInstanceType.ACCESSED, transaction);
2414 }
2415
2438 public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long sourceObjId, Long dataSourceObjId,
2439 Collection<BlackboardAttribute> attributes,
2440 Long osAccountObjId, OsAccountInstance.OsAccountInstanceType osAccountInstanceType,
2441 final CaseDbTransaction transaction) throws TskCoreException {
2442
2443 if (artifactType.getCategory() != BlackboardArtifact.Category.DATA_ARTIFACT) {
2444 throw new TskCoreException(String.format("Artifact type (name = %s) is not of Data Artifact category. ", artifactType.getTypeName()));
2445 }
2446
2447 try {
2448 CaseDbConnection connection = transaction.getConnection();
2449 long artifact_obj_id = caseDb.addObject(sourceObjId, TskData.ObjectType.ARTIFACT.getObjectType(), connection);
2450 PreparedStatement statement = caseDb.createInsertArtifactStatement(artifactType.getTypeID(), sourceObjId, artifact_obj_id, dataSourceObjId, connection);
2451
2452 connection.executeUpdate(statement);
2453 try (ResultSet resultSet = statement.getGeneratedKeys()) {
2454 resultSet.next();
2455 DataArtifact dataArtifact = new DataArtifact(caseDb, resultSet.getLong(1), //last_insert_rowid()
2456 sourceObjId, artifact_obj_id, dataSourceObjId, artifactType.getTypeID(),
2457 artifactType.getTypeName(), artifactType.getDisplayName(), BlackboardArtifact.ReviewStatus.UNDECIDED,
2458 osAccountObjId, true);
2459
2460 // Add a row in tsk_data_artifact if the os account is present
2461 if (osAccountObjId != null) {
2462 String insertDataArtifactSQL = "INSERT INTO tsk_data_artifacts (artifact_obj_id, os_account_obj_id) VALUES (?, ?)";
2463
2464 statement = connection.getPreparedStatement(insertDataArtifactSQL, Statement.NO_GENERATED_KEYS);
2465 statement.clearParameters();
2466
2467 statement.setLong(1, artifact_obj_id);
2468 statement.setLong(2, osAccountObjId);
2469 connection.executeUpdate(statement);
2470
2471 // Add an OS account instance
2472 if (Objects.nonNull(osAccountInstanceType)) {
2473 caseDb.getOsAccountManager().newOsAccountInstance(osAccountObjId, dataSourceObjId, osAccountInstanceType, connection);
2474 }
2475 }
2476
2477 // if attributes are provided, add them to the artifact.
2478 if (Objects.nonNull(attributes) && !attributes.isEmpty()) {
2479 dataArtifact.addAttributes(attributes, transaction);
2480 }
2481
2482 return dataArtifact;
2483 }
2484 } catch (SQLException ex) {
2485 throw new TskCoreException(String.format("Error creating a data artifact with type id = %d, objId = %d, and data source oj id = %d ", artifactType.getTypeID(), sourceObjId, dataSourceObjId), ex);
2486 }
2487 }
2488
2500 List<BlackboardArtifact> getArtifactsBySourceId(BlackboardArtifact.Type artifactType, long sourceObjId) throws TskCoreException {
2501 String whereClause = String.format("artifacts.obj_id = %d", sourceObjId);
2502 return getArtifactsWhere(artifactType, whereClause);
2503 }
2504
2514 List<BlackboardArtifact> getArtifactsByType(BlackboardArtifact.Type artifactType) throws TskCoreException {
2515 List<BlackboardArtifact> artifacts = new ArrayList<>();
2516 if (artifactType.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
2517 artifacts.addAll(getAnalysisResultsByType(artifactType.getTypeID()));
2518 } else {
2519 artifacts.addAll(getDataArtifacts(artifactType.getTypeID()));
2520 }
2521 return artifacts;
2522 }
2523
2541 private List<BlackboardArtifact> getArtifactsWhere(BlackboardArtifact.Type artifactType, String whereClause) throws TskCoreException {
2542 List<BlackboardArtifact> artifacts = new ArrayList<>();
2543 String whereWithType = whereClause + " AND artifacts.artifact_type_id = " + artifactType.getTypeID();
2544
2545 if (artifactType.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
2546 artifacts.addAll(getAnalysisResultsWhere(whereWithType));
2547 } else {
2548 artifacts.addAll(getDataArtifactsWhere(whereWithType));
2549 }
2550
2551 return artifacts;
2552 }
2553
2559 final public class ArtifactsPostedEvent {
2560
2561 private final String moduleName;
2562 private final ImmutableSet<BlackboardArtifact.Type> artifactTypes;
2563 private final ImmutableSet<BlackboardArtifact> artifacts;
2564 private final Long ingestJobId;
2565
2577 private ArtifactsPostedEvent(Collection<BlackboardArtifact> artifacts, String moduleName, Long ingestJobId) throws BlackboardException {
2578 Set<Integer> typeIDS = artifacts.stream()
2580 .collect(Collectors.toSet());
2581 Set<BlackboardArtifact.Type> types = new HashSet<>();
2582 for (Integer typeID : typeIDS) {
2583 try {
2584 types.add(getArtifactType(typeID));
2585 } catch (TskCoreException tskCoreException) {
2586 throw new BlackboardException("Error getting artifact type by id.", tskCoreException);
2587 }
2588 }
2589 artifactTypes = ImmutableSet.copyOf(types);
2590 this.artifacts = ImmutableSet.copyOf(artifacts);
2591 this.moduleName = moduleName;
2592 this.ingestJobId = ingestJobId;
2593 }
2594
2600 public Collection<BlackboardArtifact> getArtifacts() {
2601 return ImmutableSet.copyOf(artifacts);
2602 }
2603
2611 public Collection<BlackboardArtifact> getArtifacts(BlackboardArtifact.Type artifactType) {
2612 Set<BlackboardArtifact> tempSet = artifacts.stream()
2613 .filter(artifact -> artifact.getArtifactTypeID() == artifactType.getTypeID())
2614 .collect(Collectors.toSet());
2615 return ImmutableSet.copyOf(tempSet);
2616 }
2617
2623 public String getModuleName() {
2624 return moduleName;
2625 }
2626
2633 return ImmutableSet.copyOf(artifactTypes);
2634 }
2635
2642 public Optional<Long> getIngestJobId() {
2643 return Optional.ofNullable(ingestJobId);
2644 }
2645
2646 }
2647}
Collection< BlackboardArtifact > getArtifacts(BlackboardArtifact.Type artifactType)
Collection< BlackboardArtifact.Type > getArtifactTypes()
AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, Score score, String conclusion, String configuration, String justification, Collection< BlackboardAttribute > attributesList)
void addAttributes(Collection< BlackboardAttribute > attributes)
Score deleteAnalysisResult(long artifactObjId, CaseDbTransaction transaction)
void postArtifact(BlackboardArtifact artifact, String moduleName)
List< BlackboardArtifact > getExactMatchKeywordSearchResults(String keyword, TskData.KeywordSearchQueryType searchType, String kwsListName, Long dataSourceId)
DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long sourceObjId, Long dataSourceObjId, Collection< BlackboardAttribute > attributes, Long osAccountObjId, final CaseDbTransaction transaction)
void updateFileAttributes(long fileObjId, List< Attribute > attributes)
List< DataArtifact > getDataArtifacts(long dataSourceObjId, Integer artifactTypeID)
DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long sourceObjId, Long dataSourceObjId, Collection< BlackboardAttribute > attributes, Long osAccountId)
boolean hasAnalysisResults(long sourceObjId)
void postArtifacts(Collection< BlackboardArtifact > artifacts, String moduleName)
List< AnalysisResult > getAnalysisResultsByType(int artifactTypeId)
List< DataArtifact > getDataArtifactsWhere(String whereClause)
List< DataArtifact > getDataArtifacts(int artifactTypeID)
AnalysisResult getAnalysisResultById(long artifactObjId)
List< AnalysisResult > getAnalysisResultsByType(int artifactTypeId, long dataSourceObjId)
BlackboardArtifact.Type getArtifactType(String artTypeName)
DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long sourceObjId, Long dataSourceObjId, Collection< BlackboardAttribute > attributes, Long osAccountObjId, OsAccountInstance.OsAccountInstanceType osAccountInstanceType, final CaseDbTransaction transaction)
List< BlackboardArtifact > getArtifacts(Collection< BlackboardArtifact.Type > artifactTypes, Collection< Long > dataSourceObjIds)
BlackboardAttribute.Type getAttributeType(String attrTypeName)
void postArtifact(BlackboardArtifact artifact, String moduleName, Long ingestJobId)
long getArtifactsCount(int artifactTypeID)
List< BlackboardArtifact > getKeywordSearchResults(String keyword, String regex, TskData.KeywordSearchQueryType searchType, String kwsListName, Long dataSourceId)
List< DataArtifact > getDataArtifacts(int artifactTypeID, long dataSourceObjId)
List< BlackboardArtifact > getArtifacts(BlackboardArtifact.Type artifactType, BlackboardAttribute.Type attributeType, String value, Long dataSourceObjId, boolean showRejected)
long getArtifactsCount(int artifactTypeID, long dataSourceObjId)
boolean artifactExists(Content content, BlackboardArtifact.ARTIFACT_TYPE artifactType, Collection< BlackboardAttribute > attributes)
DataArtifact getDataArtifactById(long artifactObjId)
List< BlackboardArtifact > getArtifacts(int artifactTypeID, long dataSourceObjId)
List< BlackboardArtifact.Type > getArtifactTypesInUse(long dataSourceObjId)
Score ignoreAnalysisResultScore(long artifactObjId, boolean ignore, CaseDbTransaction transaction)
boolean artifactExists(Content content, BlackboardArtifact.Type artifactType, Collection< BlackboardAttribute > attributes)
AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, long objId, Long dataSourceObjId, Score score, String conclusion, String configuration, String justification, Collection< BlackboardAttribute > attributesList, CaseDbTransaction transaction)
BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName)
void postArtifacts(Collection< BlackboardArtifact > artifacts, String moduleName, Long ingestJobId)
List< AnalysisResult > getAnalysisResults(long dataSourceObjId, Integer artifactTypeID)
List< AnalysisResult > getAnalysisResults(long sourceObjId, int artifactTypeId)
BlackboardArtifact.Type getArtifactType(int artTypeId)
ArrayList< BlackboardAttribute > getBlackboardAttributes(final BlackboardArtifact artifact)
AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, long objId, Long dataSourceObjId, Score score, String conclusion, String configuration, String justification, Collection< BlackboardAttribute > attributesList)
List< AnalysisResult > getAnalysisResultsWhere(String whereClause)
Score deleteAnalysisResult(AnalysisResult analysisResult)
BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName, BlackboardArtifact.Category category)
boolean hasDataArtifacts(long sourceObjId)
List< AnalysisResult > getAnalysisResults(long sourceObjId)
Score ignoreAnalysisResultScore(AnalysisResult analysisResult, boolean ignore)
synchronized BlackboardAttribute.Type getOrAddAttributeType(String typeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName)
static Priority fromID(int id)
Definition Score.java:184
static Significance fromID(int id)
Definition Score.java:118

Copyright © 2011-2024 Brian Carrier. (carrier -at- sleuthkit -dot- org)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.