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 */
19 package org.sleuthkit.datamodel;
20
21 import com.google.common.annotations.Beta;
22 import com.google.common.collect.ImmutableSet;
23 import java.sql.PreparedStatement;
24 import java.sql.ResultSet;
25 import java.sql.SQLException;
26 import java.sql.Statement;
27 import java.text.MessageFormat;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Objects;
37 import java.util.Optional;
38 import java.util.Set;
39 import java.util.concurrent.ConcurrentHashMap;
40 import java.util.logging.Level;
41 import java.util.logging.Logger;
42 import java.util.stream.Collectors;
48
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 */
68
69 static final int MIN_USER_DEFINED_TYPE_ID = 10000;
70
72
80 this.caseDb = Objects.requireNonNull(casedb, "Cannot create Blackboard for null SleuthkitCase");
81 }
82
98 @Deprecated
100 postArtifacts(Collections.singleton(artifact), moduleName, null);
101 }
102
118 @Deprecated
121 }
122
139 postArtifacts(Collections.singleton(artifact), moduleName, ingestJobId);
140 }
141
159 try {
162 throw new BlackboardException(String.format(
"Failed to add events to timeline for artifact '%s'", artifact), ex);
163 }
164 }
166 }
167
184 }
185
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 Statement s = null;
209 ResultSet rs = null;
211 try {
213
214 CaseDbConnection connection = trans.getConnection();
215 s = connection.createStatement();
216 rs = connection.executeQuery(s, "SELECT artifact_type_id FROM blackboard_artifact_types WHERE type_name = '" + typeName + "'"); //NON-NLS
217 if (!rs.next()) {
218 rs.close();
219 rs = connection.executeQuery(s, "SELECT MAX(artifact_type_id) AS highest_id FROM blackboard_artifact_types");
220 int maxID = 0;
221 if (rs.next()) {
222 maxID = rs.getInt("highest_id");
223 if (maxID < MIN_USER_DEFINED_TYPE_ID) {
224 maxID = MIN_USER_DEFINED_TYPE_ID;
225 } else {
226 maxID++;
227 }
228 }
229 connection.executeUpdate(s, "INSERT INTO blackboard_artifact_types (artifact_type_id, type_name, display_name, category_type) VALUES ('" + maxID + "', '" + typeName + "', '" + displayName + "', " + category.getID() + " )"); //NON-NLS
231 this.typeIdToArtifactTypeMap.put(type.getTypeID(), type);
232 this.typeNameToArtifactTypeMap.put(type.getTypeName(), type);
234 trans = null;
235 return type;
236 } else {
238 trans = null;
239 try {
242 throw new BlackboardException("Failed to get or add artifact type: " + typeName, ex);
243 }
244 }
246 try {
247 if (trans != null) {
249 trans = null;
250 }
252 LOGGER.log(Level.SEVERE, "Error rolling back transaction", ex2);
253 }
254 throw new BlackboardException("Error adding artifact type: " + typeName, ex);
255 } finally {
256 closeResultSet(rs);
257 closeStatement(s);
258 if (trans != null) {
259 try {
262 throw new BlackboardException("Error rolling back transaction", ex);
263 }
264 }
265 }
266 }
267
279 if (this.typeNameToAttributeTypeMap.containsKey(attrTypeName)) {
280 return this.typeNameToAttributeTypeMap.get(attrTypeName);
281 }
282 CaseDbConnection connection = null;
283 Statement s = null;
284 ResultSet rs = null;
286 try {
287 connection = caseDb.getConnection();
288 s = connection.createStatement();
289 rs = connection.executeQuery(s, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types WHERE type_name = '" + attrTypeName + "'"); //NON-NLS
291 if (rs.next()) {
294 this.typeIdToAttributeTypeMap.put(type.getTypeID(), type);
295 this.typeNameToAttributeTypeMap.put(attrTypeName, type);
296 }
297 return type;
298 } catch (SQLException ex) {
300 } finally {
301 closeResultSet(rs);
302 closeStatement(s);
303 closeConnection(connection);
305 }
306 }
307
319 if (this.typeIdToAttributeTypeMap.containsKey(typeID)) {
320 return this.typeIdToAttributeTypeMap.get(typeID);
321 }
322 CaseDbConnection connection = null;
323 Statement s = null;
324 ResultSet rs = null;
326 try {
327 connection = caseDb.getConnection();
328 s = connection.createStatement();
329 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
330 BlackboardAttribute.Type type = null;
331 if (rs.next()) {
332 type = new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"),
333 rs.getString("display_name"), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type")));
334 this.typeIdToAttributeTypeMap.put(typeID, type);
335 this.typeNameToAttributeTypeMap.put(type.getTypeName(), type);
336 }
337 return type;
338 } catch (SQLException ex) {
339 throw new TskCoreException("Error getting attribute type id", ex);
340 } finally {
341 closeResultSet(rs);
342 closeStatement(s);
343 closeConnection(connection);
345 }
346 }
347
359 if (this.typeNameToArtifactTypeMap.containsKey(artTypeName)) {
360 return this.typeNameToArtifactTypeMap.get(artTypeName);
361 }
362 CaseDbConnection connection = null;
363 Statement s = null;
364 ResultSet rs = null;
366 try {
367 connection = caseDb.getConnection();
368 s = connection.createStatement();
369 rs = connection.executeQuery(s, "SELECT artifact_type_id, type_name, display_name, category_type FROM blackboard_artifact_types WHERE type_name = '" + artTypeName + "'"); //NON-NLS
371 if (rs.next()) {
373 rs.getString("type_name"), rs.getString("display_name"),
375 this.typeIdToArtifactTypeMap.put(type.getTypeID(), type);
376 this.typeNameToArtifactTypeMap.put(artTypeName, type);
377 }
378 return type;
379 } catch (SQLException ex) {
380 throw new TskCoreException(
"Error getting artifact type from the database", ex);
381 } finally {
382 closeResultSet(rs);
383 closeStatement(s);
384 closeConnection(connection);
386 }
387 }
388
401 if (this.typeIdToArtifactTypeMap.containsKey(artTypeId)) {
402 return typeIdToArtifactTypeMap.get(artTypeId);
403 }
404 CaseDbConnection connection = null;
405 Statement s = null;
406 ResultSet rs = null;
408 try {
409 connection = caseDb.getConnection();
410 s = connection.createStatement();
411 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
413 if (rs.next()) {
415 rs.getString("type_name"), rs.getString("display_name"),
417 this.typeIdToArtifactTypeMap.put(artTypeId, type);
418 this.typeNameToArtifactTypeMap.put(type.getTypeName(), type);
419 return type;
420 } else {
421 throw new TskCoreException(
"No artifact type found matching id: " + artTypeId);
422 }
423 } catch (SQLException ex) {
424 throw new TskCoreException(
"Error getting artifact type from the database", ex);
425 } finally {
426 closeResultSet(rs);
427 closeStatement(s);
428 closeConnection(connection);
430 }
431 }
432
443 CaseDbConnection connection = null;
444 Statement statement = null;
445 ResultSet rs = null;
446
447 String rowId;
449 case POSTGRESQL:
450 rowId = "attrs.CTID";
451 break;
452 case SQLITE:
453 rowId = "attrs.ROWID";
454 break;
455 default:
457 }
458
460 try {
461 connection = caseDb.getConnection();
462 statement = connection.createStatement();
463 rs = connection.executeQuery(statement, "SELECT attrs.artifact_id AS artifact_id, "
464 + "attrs.source AS source, attrs.context AS context, attrs.attribute_type_id AS attribute_type_id, "
465 + "attrs.value_type AS value_type, attrs.value_byte AS value_byte, "
466 + "attrs.value_text AS value_text, attrs.value_int32 AS value_int32, "
467 + "attrs.value_int64 AS value_int64, attrs.value_double AS value_double, "
468 + "types.type_name AS type_name, types.display_name AS display_name "
469 + "FROM blackboard_attributes AS attrs, blackboard_attribute_types AS types WHERE attrs.artifact_id = " + artifact.getArtifactID()
470 + " AND attrs.attribute_type_id = types.attribute_type_id "
471 + " ORDER BY " + rowId);
472 ArrayList<BlackboardAttribute> attributes = new ArrayList<>();
473 while (rs.next()) {
475 attr.setParentDataSourceID(artifact.getDataSourceObjectID());
476 attributes.add(attr);
477 }
478 return attributes;
479 } catch (SQLException ex) {
480 throw new TskCoreException(
"Error getting attributes for artifact, artifact id = " + artifact.getArtifactID(), ex);
481 } finally {
482 closeResultSet(rs);
483 closeStatement(statement);
484 closeConnection(connection);
486 }
487 }
488
499 @Beta
500 public <T extends BlackboardArtifact>
void loadBlackboardAttributes(List<T> arts)
throws TskCoreException {
501
502 if (arts.isEmpty()) {
503 return;
504 }
505
506 // Make a map of artifact ID to artifact
507 Map<Long, BlackboardArtifact> artifactMap = new HashMap<>();
508 for (BlackboardArtifact art : arts) {
509 artifactMap.put(art.getArtifactID(), art);
510 }
511
512 // Make a map of artifact ID to attribute list
513 Map<Long, List<BlackboardAttribute>> attributeMap = new HashMap<>();
514
515 // Get all artifact IDs as a comma-separated string
516 String idString = arts.stream().map(p -> Long.toString(p.getArtifactID())).collect(Collectors.joining(", "));
517
518 String rowId;
520 case POSTGRESQL:
521 rowId = "attrs.CTID";
522 break;
523 case SQLITE:
524 rowId = "attrs.ROWID";
525 break;
526 default:
527 throw new TskCoreException(
"Unknown database type: " + caseDb.
getDatabaseType());
528 }
529
530 // Get the attributes
531 CaseDbConnection connection = null;
532 Statement statement = null;
533 ResultSet rs = null;
535 try {
536 connection = caseDb.getConnection();
537 statement = connection.createStatement();
538 rs = connection.executeQuery(statement, "SELECT attrs.artifact_id AS artifact_id, "
539 + "attrs.source AS source, attrs.context AS context, attrs.attribute_type_id AS attribute_type_id, "
540 + "attrs.value_type AS value_type, attrs.value_byte AS value_byte, "
541 + "attrs.value_text AS value_text, attrs.value_int32 AS value_int32, "
542 + "attrs.value_int64 AS value_int64, attrs.value_double AS value_double, "
543 + "types.type_name AS type_name, types.display_name AS display_name "
544 + "FROM blackboard_attributes AS attrs, blackboard_attribute_types AS types WHERE attrs.artifact_id IN (" + idString + ") "
545 + " AND attrs.attribute_type_id = types.attribute_type_id"
546 + " ORDER BY " + rowId);
547 while (rs.next()) {
548 final BlackboardAttribute attr = createAttributeFromResultSet(rs);
549 attr.setParentDataSourceID(artifactMap.get(attr.getArtifactID()).getDataSourceObjectID());
550
551 // Collect the list of attributes for each artifact
552 if (!attributeMap.containsKey(attr.getArtifactID())) {
553 attributeMap.put(attr.getArtifactID(), new ArrayList<>());
554 }
555 attributeMap.get(attr.getArtifactID()).add(attr);
556 }
557
558 // Save the attributes to the artifacts
559 for (Long artifactID : attributeMap.keySet()) {
560 artifactMap.get(artifactID).setAttributes(attributeMap.get(artifactID));
561 }
562
563 } catch (SQLException ex) {
564 throw new TskCoreException("Error loading attributes", ex);
565 } finally {
566 closeResultSet(rs);
567 closeStatement(statement);
568 closeConnection(connection);
570 }
571 }
572
581 private BlackboardAttribute createAttributeFromResultSet(ResultSet rs) throws SQLException {
582 int attributeTypeId = rs.getInt("attribute_type_id");
583 String attributeTypeName = rs.getString("type_name");
584 BlackboardAttribute.Type attributeType;
585 if (this.typeIdToAttributeTypeMap.containsKey(attributeTypeId)) {
586 attributeType = this.typeIdToAttributeTypeMap.get(attributeTypeId);
587 } else {
588 attributeType = new BlackboardAttribute.Type(attributeTypeId, attributeTypeName,
589 rs.getString("display_name"),
590 BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getInt("value_type")));
591 this.typeIdToAttributeTypeMap.put(attributeTypeId, attributeType);
592 this.typeNameToAttributeTypeMap.put(attributeTypeName, attributeType);
593 }
594
595 return new BlackboardAttribute(
596 rs.getLong("artifact_id"),
597 attributeType,
598 rs.getString("source"),
599 rs.getString("context"),
600 rs.getInt("value_int32"),
601 rs.getLong("value_int64"),
602 rs.getDouble("value_double"),
603 rs.getString("value_text"),
604 rs.getBytes("value_byte"), caseDb
605 );
606 }
607
617 @Beta
619
621 try (CaseDbConnection connection = caseDb.getConnection()) {
623 String updateString = "UPDATE tsk_file_attributes SET value_byte = ?, value_text = ?, value_int32 = ?, "
624 + " value_int64 = ?, value_double = ? WHERE attribute_type_id = " + attr.getAttributeType().getTypeID()
625 + " AND obj_id = " + fileObjId;
626
627 try (PreparedStatement preparedStatement = connection.getPreparedStatement(updateString, Statement.NO_GENERATED_KEYS);) {
628 preparedStatement.clearParameters();
629
631 preparedStatement.setBytes(1, attr.getValueBytes());
632 } else {
633 preparedStatement.setBytes(1, null);
634 }
635
638 preparedStatement.setString(2, attr.getValueString());
639 } else {
640 preparedStatement.setString(2, null);
641 }
642
644 preparedStatement.setInt(3, attr.getValueInt());
645 } else {
646 preparedStatement.setNull(3, java.sql.Types.INTEGER);
647 }
648
651 preparedStatement.setLong(4, attr.getValueLong());
652 } else {
653 preparedStatement.setNull(4, java.sql.Types.BIGINT);
654 }
655
657 preparedStatement.setDouble(5, attr.getValueDouble());
658 } else {
659 preparedStatement.setNull(5, java.sql.Types.DOUBLE);
660 }
661
662 connection.executeUpdate(preparedStatement);
663
664 } catch (SQLException ex) {
665 throw new TskCoreException(String.format(
"Error updating attribute using query = '%s'", updateString), ex);
666 }
667 }
668 } finally {
670 }
671 }
672
683 CaseDbConnection connection = null;
684 Statement statement = null;
685 ResultSet rs = null;
687 try {
688 connection = caseDb.getConnection();
689 statement = connection.createStatement();
690 rs = connection.executeQuery(statement, "SELECT attrs.id as id, attrs.obj_id AS obj_id, "
691 + "attrs.attribute_type_id AS attribute_type_id, "
692 + "attrs.value_type AS value_type, attrs.value_byte AS value_byte, "
693 + "attrs.value_text AS value_text, attrs.value_int32 AS value_int32, "
694 + "attrs.value_int64 AS value_int64, attrs.value_double AS value_double, "
695 + "types.type_name AS type_name, types.display_name AS display_name "
696 + "FROM tsk_file_attributes AS attrs "
697 + " INNER JOIN blackboard_attribute_types AS types "
698 + " ON attrs.attribute_type_id = types.attribute_type_id "
699 + " WHERE attrs.obj_id = " + file.getId());
700
701 ArrayList<Attribute> attributes = new ArrayList<Attribute>();
702 while (rs.next()) {
703 int attributeTypeId = rs.getInt("attribute_type_id");
704 String attributeTypeName = rs.getString("type_name");
706 if (this.typeIdToAttributeTypeMap.containsKey(attributeTypeId)) {
707 attributeType = this.typeIdToAttributeTypeMap.get(attributeTypeId);
708 } else {
709 attributeType = new BlackboardAttribute.Type(attributeTypeId, attributeTypeName,
710 rs.getString("display_name"),
711 BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getInt("value_type")));
712 this.typeIdToAttributeTypeMap.put(attributeTypeId, attributeType);
713 this.typeNameToAttributeTypeMap.put(attributeTypeName, attributeType);
714 }
715
716 final Attribute attr = new Attribute(
717 rs.getLong("id"),
718 rs.getLong("obj_id"),
719 attributeType,
720 rs.getInt("value_int32"),
721 rs.getLong("value_int64"),
722 rs.getDouble("value_double"),
723 rs.getString("value_text"),
724 rs.getBytes("value_byte"), caseDb
725 );
726 attributes.add(attr);
727 }
728 return attributes;
729 } catch (SQLException ex) {
730 throw new TskCoreException("Error getting attributes for file, file id = " + file.getId(), ex);
731 } finally {
732 closeResultSet(rs);
733 closeStatement(statement);
734 closeConnection(connection);
736 }
737 }
738
748 void initBlackboardArtifactTypes(CaseDbConnection connection) throws SQLException {
750 try (Statement statement = connection.createStatement()) {
751 /*
752 * Determine which types, if any, have already been added to the
753 * case database, and load them into the type caches. For a case
754 * that is being reopened, this should reduce the number of separate
755 * INSERT staements that will be executed below.
756 */
757 ResultSet resultSet = connection.executeQuery(statement, "SELECT artifact_type_id, type_name, display_name, category_type FROM blackboard_artifact_types"); //NON-NLS
758 while (resultSet.next()) {
759 BlackboardArtifact.Type type = new BlackboardArtifact.Type(resultSet.getInt("artifact_type_id"),
760 resultSet.getString("type_name"), resultSet.getString("display_name"),
761 BlackboardArtifact.Category.fromID(resultSet.getInt("category_type")));
762 typeIdToArtifactTypeMap.put(type.getTypeID(), type);
763 typeNameToArtifactTypeMap.put(type.getTypeName(), type);
764 }
765
766 /*
767 * INSERT any missing standard types. A conflict clause is used to
768 * avoid a potential race condition. It also eliminates the need to
769 * add schema update code when new types are added.
770 *
771 * The use here of the soon to be deprecated
772 * BlackboardArtifact.ARTIFACT_TYPE enum instead of the
773 * BlackboardArtifact.Type.STANDARD_TYPES collection currently
774 * ensures that the deprecated types in the former, and not in the
775 * latter, are added to the case database.
776 */
777 for (BlackboardArtifact.ARTIFACT_TYPE type : BlackboardArtifact.ARTIFACT_TYPE.values()) {
778 if (typeIdToArtifactTypeMap.containsKey(type.getTypeID())) {
779 continue;
780 }
782 statement.execute("INSERT INTO blackboard_artifact_types (artifact_type_id, type_name, display_name, category_type) VALUES (" + type.getTypeID() + " , '" + type.getLabel() + "', '" + type.getDisplayName() + "' , " + type.getCategory().getID() + ") ON CONFLICT DO NOTHING"); //NON-NLS
783 } else {
784 statement.execute("INSERT OR IGNORE INTO blackboard_artifact_types (artifact_type_id, type_name, display_name, category_type) VALUES (" + type.getTypeID() + " , '" + type.getLabel() + "', '" + type.getDisplayName() + "' , " + type.getCategory().getID() + ")"); //NON-NLS
785 }
786 typeIdToArtifactTypeMap.put(type.getTypeID(), new BlackboardArtifact.Type(type));
787 typeNameToArtifactTypeMap.put(type.getLabel(), new BlackboardArtifact.Type(type));
788 }
790 int newPrimaryKeyIndex = Collections.max(Arrays.asList(BlackboardArtifact.ARTIFACT_TYPE.values())).getTypeID() + 1;
791 statement.execute("ALTER SEQUENCE blackboard_artifact_types_artifact_type_id_seq RESTART WITH " + newPrimaryKeyIndex); //NON-NLS
792 }
793 } finally {
795 }
796 }
797
807 void initBlackboardAttributeTypes(CaseDbConnection connection) throws SQLException {
809 try (Statement statement = connection.createStatement()) {
810 /*
811 * Determine which types, if any, have already been added to the
812 * case database, and load them into the type caches. For a case
813 * that is being reopened, this should reduce the number of separate
814 * INSERT staements that will be executed below.
815 */
816 ResultSet resultSet = connection.executeQuery(statement, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types"); //NON-NLS
817 while (resultSet.next()) {
818 BlackboardAttribute.Type type = new BlackboardAttribute.Type(resultSet.getInt("attribute_type_id"),
819 resultSet.getString("type_name"), resultSet.getString("display_name"),
820 BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(resultSet.getLong("value_type")));
821 typeIdToAttributeTypeMap.put(type.getTypeID(), type);
822 typeNameToAttributeTypeMap.put(type.getTypeName(), type);
823 }
824
825 /*
826 * INSERT any missing standard types. A conflict clause is used to
827 * avoid a potential race condition. It also eliminates the need to
828 * add schema update code when new types are added.
829 *
830 * The use here of the soon to be deprecated
831 * BlackboardAttribute.ATTRIBUTE_TYPE enum instead of the
832 * BlackboardAttribute.Type.STANDARD_TYPES collection currently
833 * ensures that the deprecated types in the former, and not in the
834 * latter, are added to the case database.
835 */
836 for (BlackboardAttribute.ATTRIBUTE_TYPE type : BlackboardAttribute.ATTRIBUTE_TYPE.values()) {
837 if (typeIdToAttributeTypeMap.containsKey(type.getTypeID())) {
838 continue;
839 }
841 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
842 } else {
843 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
844 }
845 typeIdToAttributeTypeMap.put(type.getTypeID(), new BlackboardAttribute.Type(type));
846 typeNameToAttributeTypeMap.put(type.getLabel(), new BlackboardAttribute.Type(type));
847 }
849 int newPrimaryKeyIndex = Collections.max(Arrays.asList(BlackboardAttribute.ATTRIBUTE_TYPE.values())).getTypeID() + 1;
850 statement.execute("ALTER SEQUENCE blackboard_attribute_types_attribute_type_id_seq RESTART WITH " + newPrimaryKeyIndex); //NON-NLS
851 }
852 } finally {
854 }
855 }
856
880 String conclusion, String configuration, String justification, Collection<BlackboardAttribute> attributesList)
882
884 throw new BlackboardException(String.format("Artifact type (name = %s) is not of Analysis Result category. ", artifactType.getTypeName()));
885 }
886
888 try {
890 conclusion, configuration, justification, attributesList, transaction);
892 return analysisResult;
894 try {
897 LOGGER.log(Level.SEVERE, "Failed to rollback transaction after exception. "
898 + "Error invoking newAnalysisResult with dataSourceObjId: "
899 + (dataSourceObjId == null ? "<null>" : dataSourceObjId)
900 + ", sourceObjId: " + objId, ex2);
901 }
902 throw ex;
903 }
904 }
905
929 String conclusion, String configuration, String justification, Collection<BlackboardAttribute> attributesList,
CaseDbTransaction transaction)
throws BlackboardException {
930
932 throw new BlackboardException(String.format("Artifact type (name = %s) is not of Analysis Result category. ", artifactType.getTypeName()));
933 }
934
935 try {
936 // add analysis result
937 AnalysisResult analysisResult = caseDb.newAnalysisResult(artifactType, objId, dataSourceObjId, score, conclusion, configuration, justification, transaction.getConnection());
938
939 // add the given attributes
940 if (attributesList != null && !attributesList.isEmpty()) {
942 }
943
944 // update the final score for the object
945 Score aggregateScore = caseDb.
getScoringManager().updateAggregateScoreAfterAddition(objId, dataSourceObjId, analysisResult.
getScore(), transaction);
946
947 // return the analysis result and the current aggregate score.
949
951 throw new BlackboardException("Failed to add analysis result.", ex);
952 }
953 }
954
970
972 try {
975 transaction = null;
976
977 return score;
978 } finally {
979 if (transaction != null) {
981 }
982 }
983 }
984
999
1000 List<AnalysisResult> analysisResults =
getAnalysisResultsWhere(
" artifacts.artifact_obj_id = " + artifactObjId, transaction.getConnection());
1001
1002 if (analysisResults.isEmpty()) {
1003 throw new TskCoreException(String.format(
"Analysis Result not found for artifact obj id %d", artifactObjId));
1004 }
1005
1007 }
1008
1023
1024 try {
1025 CaseDbConnection connection = transaction.getConnection();
1026
1027 // delete the blackboard artifacts row. This will also delete the tsk_analysis_result row
1028 String deleteSQL = "DELETE FROM blackboard_artifacts WHERE artifact_obj_id = ?";
1029
1030 PreparedStatement deleteStatement = connection.getPreparedStatement(deleteSQL, Statement.RETURN_GENERATED_KEYS);
1031 deleteStatement.clearParameters();
1032 deleteStatement.setLong(1, analysisResult.getId());
1033
1034 deleteStatement.executeUpdate();
1035
1036 // register the deleted result with the transaction so an event can be fired for it.
1037 transaction.registerDeletedAnalysisResult(analysisResult.getObjectID());
1038
1039 return caseDb.
getScoringManager().updateAggregateScoreAfterDeletion(analysisResult.getObjectID(), analysisResult.getDataSourceObjectID(), transaction);
1040
1041 } catch (SQLException ex) {
1042 throw new TskCoreException(String.format("Error deleting analysis result with artifact obj id %d", analysisResult.getId()), ex);
1043 }
1044 }
1045
1046 private final static String ANALYSIS_RESULT_QUERY_STRING_GENERIC = "SELECT DISTINCT artifacts.artifact_id AS artifact_id, " //NON-NLS
1047 + " 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, "
1048 + " types.type_name AS type_name, types.display_name AS display_name, types.category_type as category_type,"//NON-NLS
1049 + " artifacts.review_status_id AS review_status_id, " //NON-NLS
1050 + " results.conclusion AS conclusion, results.significance AS significance, results.priority AS priority, "
1051 + " results.configuration AS configuration, results.justification AS justification "
1052 + " FROM blackboard_artifacts AS artifacts "
1053 + " JOIN blackboard_artifact_types AS types " //NON-NLS
1054 + " ON artifacts.artifact_type_id = types.artifact_type_id" //NON-NLS
1055 + " LEFT JOIN tsk_analysis_results AS results "
1056 + " ON artifacts.artifact_obj_id = results.artifact_obj_id "; //NON-NLS
1057
1058 private final static String ANALYSIS_RESULT_QUERY_STRING_WITH_ATTRIBUTES
1059 = ANALYSIS_RESULT_QUERY_STRING_GENERIC
1060 + " JOIN blackboard_attributes AS attributes " //NON-NLS
1061 + " ON artifacts.artifact_id = attributes.artifact_id " //NON-NLS
1062 + " WHERE types.category_type = " + BlackboardArtifact.Category.ANALYSIS_RESULT.getID(); // NON-NLS
1063
1064 private final static String ANALYSIS_RESULT_QUERY_STRING_WHERE
1065 = ANALYSIS_RESULT_QUERY_STRING_GENERIC
1066 + " WHERE artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() //NON-NLS
1067 + " AND types.category_type = " + BlackboardArtifact.Category.ANALYSIS_RESULT.getID(); // NON-NLS
1068
1081 }
1082
1095 return getAnalysisResultsWhere(
" artifacts.artifact_type_id = " + artifactTypeId +
" AND artifacts.data_source_obj_id = " + dataSourceObjId);
1096 }
1097
1113 try (CaseDbConnection connection = caseDb.getConnection()) {
1114 String whereClause = " artifacts.data_source_obj_id = " + dataSourceObjId;
1115 if (artifactTypeID != null) {
1116 whereClause += " AND artifacts.artifact_type_id = " + artifactTypeID;
1117 }
1119 } finally {
1121 }
1122 }
1123
1136 }
1137
1148 List<DataArtifact> getDataArtifactsBySource(
long sourceObjId)
throws TskCoreException {
1150 try (CaseDbConnection connection = caseDb.getConnection()) {
1152 } finally {
1154 }
1155 }
1156
1168 }
1169
1182 }
1183
1197 String queryString = "SELECT COUNT(*) AS count " //NON-NLS
1198 + " FROM blackboard_artifacts AS arts "
1199 + " JOIN blackboard_artifact_types AS types " //NON-NLS
1200 + " ON arts.artifact_type_id = types.artifact_type_id" //NON-NLS
1201 + " WHERE types.category_type = " + category.getID()
1202 + " AND arts.obj_id = " + sourceObjId;
1203
1206 Statement statement = connection.createStatement();
1207 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
1208 if (resultSet.next()) {
1209 return resultSet.getLong("count") > 0;
1210 }
1211 return false;
1212 } catch (SQLException ex) {
1213 throw new TskCoreException("Error getting artifact types is use for data source." + ex.getMessage(), ex);
1214 } finally {
1216 }
1217 }
1218
1231 List<AnalysisResult>
getAnalysisResults(
long sourceObjId, CaseDbConnection connection)
throws TskCoreException {
1233 }
1234
1247 // Get the artifact type to check that it in the analysis result category.
1250 throw new TskCoreException(String.format(
"Artifact type id %d is not in analysis result catgeory.", artifactTypeId));
1251 }
1252
1253 String whereClause = " types.artifact_type_id = " + artifactTypeId
1254 + " AND artifacts.obj_id = " + sourceObjId;
1256 }
1257
1271 try (CaseDbConnection connection = caseDb.getConnection()) {
1273 } finally {
1275 }
1276 }
1277
1291
1292 final String queryString = ANALYSIS_RESULT_QUERY_STRING_WHERE
1293 + " AND " + whereClause;
1294
1295 try (Statement statement = connection.createStatement();
1296 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
1297
1298 List<AnalysisResult> analysisResults = resultSetToAnalysisResults(resultSet);
1299 return analysisResults;
1300 } catch (SQLException ex) {
1301 throw new TskCoreException(String.format("Error getting analysis results for WHERE clause = '%s'", whereClause), ex);
1302 }
1303 }
1304
1315
1316 String whereClause = " artifacts.artifact_obj_id = " + artifactObjId;
1318
1319 if (results.isEmpty()) { // throw an error if no analysis result found by id.
1320 throw new TskCoreException(String.format(
"Error getting analysis result with id = '%d'", artifactObjId));
1321 }
1322 if (results.size() > 1) { // should not happen - throw an error
1323 throw new TskCoreException(String.format(
"Multiple analysis results found with id = '%d'", artifactObjId));
1324 }
1325
1326 return results.get(0);
1327 }
1328
1344 private List<AnalysisResult> resultSetToAnalysisResults(ResultSet resultSet)
throws SQLException,
TskCoreException {
1345 ArrayList<AnalysisResult> analysisResults = new ArrayList<>();
1346
1347 while (resultSet.next()) {
1348 analysisResults.add(
new AnalysisResult(caseDb, resultSet.getLong(
"artifact_id"), resultSet.getLong(
"obj_id"),
1349 resultSet.getLong("artifact_obj_id"),
1350 resultSet.getObject("data_source_obj_id") != null ? resultSet.getLong("data_source_obj_id") : null,
1351 resultSet.getInt("artifact_type_id"), resultSet.getString("type_name"), resultSet.getString("display_name"),
1354 resultSet.getString("conclusion"), resultSet.getString("configuration"), resultSet.getString("justification")));
1355 } //end for each resultSet
1356
1357 return analysisResults;
1358 }
1359
1360 private final static String DATA_ARTIFACT_QUERY_STRING_GENERIC = "SELECT DISTINCT artifacts.artifact_id AS artifact_id, " //NON-NLS
1361 + "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
1362 + " types.type_name AS type_name, types.display_name AS display_name, types.category_type as category_type,"//NON-NLS
1363 + " artifacts.review_status_id AS review_status_id, " //NON-NLS
1364 + " data_artifacts.os_account_obj_id as os_account_obj_id " //NON-NLS
1365 + " FROM blackboard_artifacts AS artifacts " //NON-NLS
1366 + " JOIN blackboard_artifact_types AS types " //NON-NLS
1367 + " ON artifacts.artifact_type_id = types.artifact_type_id" //NON-NLS
1368 + " LEFT JOIN tsk_data_artifacts AS data_artifacts " //NON-NLS
1369 + " ON artifacts.artifact_obj_id = data_artifacts.artifact_obj_id "; //NON-NLS
1370
1371 private final static String DATA_ARTIFACT_QUERY_STRING_WITH_ATTRIBUTES
1372 = DATA_ARTIFACT_QUERY_STRING_GENERIC
1373 + " JOIN blackboard_attributes AS attributes " //NON-NLS
1374 + " ON artifacts.artifact_id = attributes.artifact_id " //NON-NLS
1375 + " WHERE types.category_type = " + BlackboardArtifact.Category.DATA_ARTIFACT.getID(); // NON-NLS
1376
1377 private final static String DATA_ARTIFACT_QUERY_STRING_WHERE
1378 = DATA_ARTIFACT_QUERY_STRING_GENERIC
1379 + " WHERE artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() //NON-NLS
1380 + " AND types.category_type = " + BlackboardArtifact.Category.DATA_ARTIFACT.getID(); // NON-NLS
1381
1396 try (CaseDbConnection connection = caseDb.getConnection()) {
1397 String whereClause = " artifacts.data_source_obj_id = " + dataSourceObjId;
1398 if (artifactTypeID != null) {
1399 whereClause += " AND artifacts.artifact_type_id = " + artifactTypeID;
1400 }
1402 } finally {
1404 }
1405 }
1406
1419
1420 // Get the artifact type to check that it in the data artifact category.
1423 throw new TskCoreException(String.format(
"Artifact type id %d is not in data artifact catgeory.", artifactTypeID));
1424 }
1425
1427 try (CaseDbConnection connection = caseDb.getConnection()) {
1428 String whereClause = "artifacts.data_source_obj_id = " + dataSourceObjId
1429 + " AND artifacts.artifact_type_id = " + artifactTypeID;
1430
1432 } finally {
1434 }
1435 }
1436
1448 // Get the artifact type to check that it in the data artifact category.
1451 throw new TskCoreException(String.format(
"Artifact type id %d is not in data artifact catgeory.", artifactTypeID));
1452 }
1453
1455 try (CaseDbConnection connection = caseDb.getConnection()) {
1456 String whereClause = " artifacts.artifact_type_id = " + artifactTypeID;
1457
1459 } finally {
1461 }
1462 }
1463
1476 try (CaseDbConnection connection = caseDb.getConnection()) {
1477 String whereClause = " artifacts.artifact_obj_id = " + artifactObjId;
1478
1480 if (artifacts.isEmpty()) { // throw an error if no analysis result found by id.
1481 throw new TskCoreException(String.format(
"Error getting data artifact with id = '%d'", artifactObjId));
1482 }
1483 if (artifacts.size() > 1) { // should not happen - throw an error
1484 throw new TskCoreException(String.format(
"Multiple data artifacts found with id = '%d'", artifactObjId));
1485 }
1486
1487 return artifacts.get(0);
1488 } finally {
1490 }
1491 }
1492
1505 try (CaseDbConnection connection = caseDb.getConnection()) {
1507 } finally {
1509 }
1510 }
1511
1525
1526 final String queryString = DATA_ARTIFACT_QUERY_STRING_WHERE
1527 + " AND " + whereClause + " ";
1528
1529 try (Statement statement = connection.createStatement();
1530 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
1531
1532 List<DataArtifact> dataArtifacts = resultSetToDataArtifacts(resultSet);
1533 return dataArtifacts;
1534 } catch (SQLException ex) {
1535 throw new TskCoreException(String.format("Error getting data artifacts with queryString = %s", queryString), ex);
1536 }
1537 }
1538
1554 private List<DataArtifact> resultSetToDataArtifacts(ResultSet resultSet) throws SQLException, TskCoreException {
1555 ArrayList<DataArtifact> dataArtifacts = new ArrayList<>();
1556
1557 while (resultSet.next()) {
1558
1559 Long osAccountObjId = resultSet.getLong("os_account_obj_id");
1560 if (resultSet.wasNull()) {
1561 osAccountObjId = null;
1562 }
1563
1564 dataArtifacts.add(new DataArtifact(caseDb, resultSet.getLong("artifact_id"), resultSet.getLong("obj_id"),
1565 resultSet.getLong("artifact_obj_id"),
1566 resultSet.getObject("data_source_obj_id") != null ? resultSet.getLong("data_source_obj_id") : null,
1567 resultSet.getInt("artifact_type_id"), resultSet.getString("type_name"), resultSet.getString("display_name"),
1568 BlackboardArtifact.ReviewStatus.withID(resultSet.getInt("review_status_id")), osAccountObjId, false));
1569 } //end for each resultSet
1570
1571 return dataArtifacts;
1572 }
1573
1592 // check local cache
1593 if (typeNameToAttributeTypeMap.containsKey(typeName)) {
1594 return typeNameToAttributeTypeMap.get(typeName);
1595 }
1596
1598 try {
1600 String matchingAttrQuery = "SELECT attribute_type_id, type_name, display_name, value_type "
1601 + "FROM blackboard_attribute_types WHERE type_name = ?";
1602 // find matching attribute name
1603 PreparedStatement query = trans.getConnection().getPreparedStatement(matchingAttrQuery, Statement.RETURN_GENERATED_KEYS);
1604 query.clearParameters();
1605 query.setString(1, typeName);
1606 try (ResultSet rs = query.executeQuery()) {
1607 // if previously existing, commit the results and return the attribute type
1608 if (rs.next()) {
1610 trans = null;
1612 rs.getInt("attribute_type_id"),
1613 rs.getString("type_name"),
1614 rs.getString("display_name"),
1616 );
1617
1618 this.typeIdToAttributeTypeMap.put(foundType.getTypeID(), foundType);
1619 this.typeNameToAttributeTypeMap.put(foundType.getTypeName(), foundType);
1620
1621 return foundType;
1622 }
1623 }
1624
1625 // if not found in database, insert
1626 String insertStatement = "INSERT INTO blackboard_attribute_types (attribute_type_id, type_name, display_name, value_type) VALUES (\n"
1627 // 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
1628 + "(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"
1629 // typeName, displayName, valueType
1630 + "?, ?, ?)";
1631
1632 PreparedStatement insertPreparedStatement = trans.getConnection().getPreparedStatement(insertStatement, Statement.RETURN_GENERATED_KEYS);
1633 insertPreparedStatement.clearParameters();
1634 insertPreparedStatement.setString(1, typeName);
1635 insertPreparedStatement.setString(2, displayName);
1636 insertPreparedStatement.setLong(3, valueType.getType());
1637
1638 int numUpdated = insertPreparedStatement.executeUpdate();
1639
1640 // get id for inserted to create new attribute.
1641 Integer attrId = null;
1642
1643 if (numUpdated > 0) {
1644 try (ResultSet insertResult = insertPreparedStatement.getGeneratedKeys()) {
1645 if (insertResult.next()) {
1646 attrId = insertResult.getInt(1);
1647 }
1648 }
1649 }
1650
1651 if (attrId == null) {
1652 throw new BlackboardException(MessageFormat.format(
1653 "Error adding attribute type. Item with name {0} was not inserted successfully into the database.", typeName));
1654 }
1655
1657 trans = null;
1658
1660 this.typeIdToAttributeTypeMap.put(type.getTypeID(), type);
1661 this.typeNameToAttributeTypeMap.put(type.getTypeName(), type);
1662 return type;
1664 throw new BlackboardException("Error adding attribute type: " + typeName, ex);
1665 } finally {
1666 try {
1667 if (trans != null) {
1669 trans = null;
1670 }
1672 LOGGER.log(Level.SEVERE, "Error rolling back transaction", ex2);
1673 }
1674 }
1675 }
1676
1689
1690 final String queryString = "SELECT DISTINCT arts.artifact_type_id AS artifact_type_id, "
1691 + "types.type_name AS type_name, "
1692 + "types.display_name AS display_name, "
1693 + "types.category_type AS category_type "
1694 + "FROM blackboard_artifact_types AS types "
1695 + "INNER JOIN blackboard_artifacts AS arts "
1696 + "ON arts.artifact_type_id = types.artifact_type_id "
1697 + "WHERE arts.data_source_obj_id = " + dataSourceObjId;
1698
1701 Statement statement = connection.createStatement();
1702 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
1703
1705 while (resultSet.next()) {
1707 resultSet.getString("type_name"), resultSet.getString("display_name"),
1709 }
1710 return uniqueArtifactTypes;
1711 } catch (SQLException ex) {
1712 throw new TskCoreException(
"Error getting artifact types is use for data source." + ex.getMessage(), ex);
1713 } finally {
1715 }
1716 }
1717
1731 return getArtifactsCountHelper(artifactTypeID,
1732 "blackboard_artifacts.data_source_obj_id = '" + dataSourceObjId + "';");
1733 }
1734
1747 return getArtifactsCountHelper(artifactTypeID, null);
1748 }
1749
1763 String whereClause = String.format("artifacts.data_source_obj_id = %d", dataSourceObjId);
1764 return getArtifactsWhere(
getArtifactType(artifactTypeID), whereClause);
1765 }
1766
1779 public List<BlackboardArtifact>
getArtifacts(Collection<BlackboardArtifact.Type> artifactTypes,
1781
1782 if (artifactTypes.isEmpty() || dataSourceObjIds.isEmpty()) {
1783 return new ArrayList<>();
1784 }
1785
1786 String analysisResultQuery = "";
1787 String dataArtifactQuery = "";
1788
1791 if (!analysisResultQuery.isEmpty()) {
1792 analysisResultQuery += " OR ";
1793 }
1794 analysisResultQuery += "types.artifact_type_id = " + type.getTypeID();
1795 } else {
1796 if (!dataArtifactQuery.isEmpty()) {
1797 dataArtifactQuery += " OR ";
1798 }
1799 dataArtifactQuery += "types.artifact_type_id = " + type.getTypeID();
1800 }
1801 }
1802
1803 String dsQuery = "";
1804 for (long dsId : dataSourceObjIds) {
1805 if (!dsQuery.isEmpty()) {
1806 dsQuery += " OR ";
1807 }
1808 dsQuery += "artifacts.data_source_obj_id = " + dsId;
1809 }
1810
1811 List<BlackboardArtifact> artifacts = new ArrayList<>();
1812
1813 if (!analysisResultQuery.isEmpty()) {
1814 String fullQuery = "( " + analysisResultQuery + " ) AND (" + dsQuery + ") ";
1816 }
1817
1818 if (!dataArtifactQuery.isEmpty()) {
1819 String fullQuery = "( " + dataArtifactQuery + " ) AND (" + dsQuery + ") ";
1821 }
1822
1823 return artifacts;
1824 }
1825
1845
1846 String query = " AND artifacts.artifact_type_id = " + artifactType.getTypeID() //NON-NLS
1847 + " AND attributes.attribute_type_id = " + attributeType.getTypeID() //NON-NLS
1848 + ((value == null || value.isEmpty()) ? "" : " AND attributes.value_text = '" + value + "'") //NON-NLS
1850 + (dataSourceObjId != null ? " AND artifacts.data_source_obj_id = " + dataSourceObjId : ""); //NON-NLS
1851
1852 List<BlackboardArtifact> artifacts = new ArrayList<>();
1854
1856 ? ANALYSIS_RESULT_QUERY_STRING_WITH_ATTRIBUTES + query
1857 : DATA_ARTIFACT_QUERY_STRING_WITH_ATTRIBUTES + query);
1858
1859 try (CaseDbConnection connection = caseDb.getConnection()) {
1860 try (Statement statement = connection.createStatement();
1861 ResultSet resultSet = connection.executeQuery(statement, finalQuery);) {
1862
1864 artifacts.addAll(resultSetToAnalysisResults(resultSet));
1865 } else {
1866 artifacts.addAll(resultSetToDataArtifacts(resultSet));
1867 }
1868 } catch (SQLException ex) {
1869 throw new TskCoreException(String.format(
"Error getting results with queryString = '%s'", finalQuery), ex);
1870 }
1871 } finally {
1873 }
1874 return artifacts;
1875 }
1876
1900 }
1901
1928
1929 String dataSourceClause = dataSourceId == null
1930 ? ""
1931 : " AND artifacts.data_source_obj_id = ? "; // dataSourceId
1932
1933 String kwsListClause = (kwsListName == null || kwsListName.isEmpty()
1934 ? " WHERE r.set_name IS NULL "
1935 : " WHERE r.set_name = ? ");
1936
1937 String keywordClause = (keyword == null || keyword.isEmpty()
1938 ? ""
1939 : " AND r.keyword = ? ");
1940
1941 String searchTypeClause = (searchType == null
1942 ? ""
1943 : " AND r.search_type = ? ");
1944
1945 String regexClause = (regex == null || regex.isEmpty()
1946 ? ""
1947 : " AND r.regexp_str = ? ");
1948
1949 String query = "SELECT r.* FROM ( "
1950 + " SELECT DISTINCT artifacts.artifact_id AS artifact_id, "
1951 + " artifacts.obj_id AS obj_id, "
1952 + " artifacts.artifact_obj_id AS artifact_obj_id, "
1953 + " artifacts.data_source_obj_id AS data_source_obj_id, "
1954 + " artifacts.artifact_type_id AS artifact_type_id, "
1955 + " types.type_name AS type_name, "
1956 + " types.display_name AS display_name, "
1957 + " types.category_type as category_type,"
1958 + " artifacts.review_status_id AS review_status_id, "
1959 + " results.conclusion AS conclusion, "
1960 + " results.significance AS significance, "
1961 + " results.priority AS priority, "
1962 + " results.configuration AS configuration, "
1963 + " results.justification AS justification, "
1964 + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = "
1966 + " (SELECT value_int32 FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = "
1968 + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = "
1970 + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = "
1972 + " FROM blackboard_artifacts artifacts "
1973 + " JOIN blackboard_artifact_types AS types "
1974 + " ON artifacts.artifact_type_id = types.artifact_type_id "
1975 + " LEFT JOIN tsk_analysis_results AS results "
1976 + " ON artifacts.artifact_obj_id = results.artifact_obj_id "
1979 + dataSourceClause + " ) r "
1980 + kwsListClause
1981 + keywordClause
1982 + searchTypeClause
1983 + regexClause;
1984
1985 List<BlackboardArtifact> artifacts = new ArrayList<>();
1987 try (CaseDbConnection connection = caseDb.getConnection()) {
1988
1989 try {
1990 PreparedStatement preparedStatement = connection.getPreparedStatement(query, Statement.RETURN_GENERATED_KEYS);
1991 preparedStatement.clearParameters();
1992 int paramIdx = 0;
1993 if (dataSourceId != null) {
1994 preparedStatement.setLong(++paramIdx, dataSourceId);
1995 }
1996
1997 if (!(kwsListName == null || kwsListName.isEmpty())) {
1998 preparedStatement.setString(++paramIdx, kwsListName);
1999 }
2000
2001 if (!(keyword == null || keyword.isEmpty())) {
2002 preparedStatement.setString(++paramIdx, keyword);
2003 }
2004
2005 if (searchType != null) {
2006 preparedStatement.setInt(++paramIdx, searchType.getType());
2007 }
2008
2009 if (!(regex == null || regex.isEmpty())) {
2010 preparedStatement.setString(++paramIdx, regex);
2011 }
2012
2013 try (ResultSet resultSet = connection.executeQuery(preparedStatement)) {
2014 artifacts.addAll(resultSetToAnalysisResults(resultSet));
2015 }
2016
2017 } catch (SQLException ex) {
2018 throw new TskCoreException(String.format(
"Error getting keyword search results with queryString = '%s'", query), ex);
2019 }
2020 } finally {
2022 }
2023 return artifacts;
2024 }
2025
2039 private long getArtifactsCountHelper(
int artifactTypeID, String whereClause)
throws TskCoreException {
2040 String queryString = "SELECT COUNT(*) AS count FROM blackboard_artifacts "
2041 + "WHERE blackboard_artifacts.artifact_type_id = " + artifactTypeID
2043
2044 if (whereClause != null) {
2045 queryString += " AND " + whereClause;
2046 }
2047
2049 try (SleuthkitCase.CaseDbConnection connection = caseDb.getConnection();
2050 Statement statement = connection.createStatement();
2051 ResultSet resultSet = connection.executeQuery(statement, queryString);) {
2052 long count = 0;
2053 if (resultSet.next()) {
2054 count = resultSet.getLong("count");
2055 }
2056 return count;
2057 } catch (SQLException ex) {
2058 throw new TskCoreException("Error getting artifact types is use for data source." + ex.getMessage(), ex);
2059 } finally {
2061 }
2062 }
2063
2078 List<BlackboardArtifact> existingArtifacts = content.getArtifacts(artifactType.getTypeID());
2080 if (attributesMatch(artifact.getAttributes(), attributes)) {
2081 return true;
2082 }
2083 }
2084 return false;
2085 }
2086
2102 @Deprecated
2105 }
2106
2116 private boolean attributesMatch(Collection<BlackboardAttribute> fileAttributesList, Collection<BlackboardAttribute> expectedAttributesList) {
2118 boolean match = false;
2121 if (attributeType.getTypeID() != expectedAttribute.getAttributeType().getTypeID()) {
2122 continue;
2123 }
2124
2125 Object fileAttributeValue;
2126 Object expectedAttributeValue;
2127 switch (attributeType.getValueType()) {
2128 case BYTE:
2129 fileAttributeValue = fileAttribute.getValueBytes();
2130 expectedAttributeValue = expectedAttribute.getValueBytes();
2131 break;
2132 case DOUBLE:
2133 fileAttributeValue = fileAttribute.getValueDouble();
2134 expectedAttributeValue = expectedAttribute.getValueDouble();
2135 break;
2136 case INTEGER:
2137 fileAttributeValue = fileAttribute.getValueInt();
2138 expectedAttributeValue = expectedAttribute.getValueInt();
2139 break;
2140 case LONG: // Fall-thru
2141 case DATETIME:
2142 fileAttributeValue = fileAttribute.getValueLong();
2143 expectedAttributeValue = expectedAttribute.getValueLong();
2144 break;
2145 case STRING: // Fall-thru
2146 case JSON:
2147 fileAttributeValue = fileAttribute.getValueString();
2148 expectedAttributeValue = expectedAttribute.getValueString();
2149 break;
2150 default:
2151 fileAttributeValue = fileAttribute.getDisplayString();
2152 expectedAttributeValue = expectedAttribute.getDisplayString();
2153 break;
2154 }
2155
2156 /*
2157 * If the exact attribute was found, mark it as a match to
2158 * continue looping through the expected attributes list.
2159 */
2160 if (fileAttributeValue instanceof byte[]) {
2161 if (Arrays.equals((byte[]) fileAttributeValue, (byte[]) expectedAttributeValue)) {
2162 match = true;
2163 break;
2164 }
2165 } else if (fileAttributeValue.equals(expectedAttributeValue)) {
2166 match = true;
2167 break;
2168 }
2169 }
2170 if (!match) {
2171 /*
2172 * The exact attribute type/value combination was not found.
2173 */
2174 return false;
2175 }
2176 }
2177
2178 /*
2179 * All attribute type/value combinations were found in the provided
2180 * attributes list.
2181 */
2182 return true;
2183
2184 }
2185
2189 public static final class BlackboardException
extends Exception {
2190
2191 private static final long serialVersionUID = 1L;
2192
2198 BlackboardException(String message) {
2199 super(message);
2200 }
2201
2209 BlackboardException(String message, Throwable cause) {
2210 super(message, cause);
2211 }
2212 }
2213
2231 Collection<BlackboardAttribute> attributes, Long osAccountId)
throws TskCoreException {
2232
2234 throw new TskCoreException(String.format(
"Artifact type (name = %s) is not of Data Artifact category. ", artifactType.getTypeName()));
2235 }
2236
2238 try {
2240 attributes, osAccountId, transaction);
2242 return dataArtifact;
2244 try {
2247 LOGGER.log(Level.SEVERE, "Failed to rollback transaction after exception. "
2248 + "Error invoking newDataArtifact with dataSourceObjId: " + dataSourceObjId + ", sourceObjId: " + sourceObjId, ex2);
2249 }
2250 throw ex;
2251 }
2252 }
2253
2277
2280 }
2281
2305 Collection<BlackboardAttribute> attributes,
2308
2310 throw new TskCoreException(String.format(
"Artifact type (name = %s) is not of Data Artifact category. ", artifactType.getTypeName()));
2311 }
2312
2313 try {
2314 CaseDbConnection connection = transaction.getConnection();
2316 PreparedStatement statement = caseDb.createInsertArtifactStatement(artifactType.getTypeID(), sourceObjId, artifact_obj_id, dataSourceObjId, connection);
2317
2318 connection.executeUpdate(statement);
2319 try (ResultSet resultSet = statement.getGeneratedKeys()) {
2320 resultSet.next();
2322 sourceObjId, artifact_obj_id, dataSourceObjId, artifactType.getTypeID(),
2324 osAccountObjId, true);
2325
2326 // Add a row in tsk_data_artifact if the os account is present
2327 if (osAccountObjId != null) {
2328 String insertDataArtifactSQL = "INSERT INTO tsk_data_artifacts (artifact_obj_id, os_account_obj_id) VALUES (?, ?)";
2329
2330 statement = connection.getPreparedStatement(insertDataArtifactSQL, Statement.NO_GENERATED_KEYS);
2331 statement.clearParameters();
2332
2333 statement.setLong(1, artifact_obj_id);
2334 statement.setLong(2, osAccountObjId);
2335 connection.executeUpdate(statement);
2336
2337 // Add an OS account instance
2338 if (Objects.nonNull(osAccountInstanceType)) {
2340 }
2341 }
2342
2343 // if attributes are provided, add them to the artifact.
2344 if (Objects.nonNull(attributes) && !attributes.isEmpty()) {
2346 }
2347
2348 return dataArtifact;
2349 }
2350 } catch (SQLException ex) {
2351 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);
2352 }
2353 }
2354
2367 String whereClause = String.format("artifacts.obj_id = %d", sourceObjId);
2368 return getArtifactsWhere(artifactType, whereClause);
2369 }
2370
2380 List<BlackboardArtifact> getArtifactsByType(BlackboardArtifact.Type artifactType) throws TskCoreException {
2381 List<BlackboardArtifact> artifacts = new ArrayList<>();
2382 if (artifactType.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
2384 } else {
2386 }
2387 return artifacts;
2388 }
2389
2407 private List<BlackboardArtifact> getArtifactsWhere(BlackboardArtifact.Type artifactType, String whereClause) throws TskCoreException {
2408 List<BlackboardArtifact> artifacts = new ArrayList<>();
2409 String whereWithType = whereClause + " AND artifacts.artifact_type_id = " + artifactType.getTypeID();
2410
2411 if (artifactType.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
2413 } else {
2415 }
2416
2417 return artifacts;
2418 }
2419
2426
2427 private final String moduleName;
2429 private final ImmutableSet<BlackboardArtifact> artifacts;
2430 private final Long ingestJobId;
2431
2443 private ArtifactsPostedEvent(Collection<BlackboardArtifact> artifacts, String moduleName, Long ingestJobId)
throws BlackboardException {
2444 Set<Integer> typeIDS = artifacts.stream()
2446 .collect(Collectors.toSet());
2448 for (Integer typeID : typeIDS) {
2449 try {
2451 } catch (TskCoreException tskCoreException) {
2452 throw new BlackboardException("Error getting artifact type by id.", tskCoreException);
2453 }
2454 }
2455 artifactTypes = ImmutableSet.copyOf(types);
2456 this.artifacts = ImmutableSet.copyOf(artifacts);
2457 this.moduleName = moduleName;
2458 this.ingestJobId = ingestJobId;
2459 }
2460
2467 return ImmutableSet.copyOf(artifacts);
2468 }
2469
2478 Set<BlackboardArtifact> tempSet = artifacts.stream()
2479 .filter(artifact -> artifact.getArtifactTypeID() == artifactType.getTypeID())
2480 .collect(Collectors.toSet());
2481 return ImmutableSet.copyOf(tempSet);
2482 }
2483
2490 return moduleName;
2491 }
2492
2499 return ImmutableSet.copyOf(artifactTypes);
2500 }
2501
2509 return Optional.ofNullable(ingestJobId);
2510 }
2511
2512 }
2513 }
static Priority fromID(int id)
List< BlackboardArtifact > getArtifacts(Collection< BlackboardArtifact.Type > artifactTypes, Collection< Long > dataSourceObjIds)
static Significance fromID(int id)
AnalysisResult getAnalysisResultById(long artifactObjId)
void postArtifact(BlackboardArtifact artifact, String moduleName)
CaseDbTransaction beginTransaction()
List< DataArtifact > getDataArtifacts(int artifactTypeID, long dataSourceObjId)
List< DataArtifact > getDataArtifacts(long dataSourceObjId, Integer artifactTypeID)
boolean hasAnalysisResults(long sourceObjId)
void postArtifacts(Collection< BlackboardArtifact > artifacts, String moduleName)
static final Type TSK_KEYWORD_HIT
static Category fromID(int id)
void addAttributes(Collection< BlackboardAttribute > attributes)
DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long sourceObjId, Long dataSourceObjId, Collection< BlackboardAttribute > attributes, Long osAccountId)
ArrayList< BlackboardAttribute > getBlackboardAttributes(final BlackboardArtifact artifact)
DataArtifact getDataArtifactById(long artifactObjId)
Collection< BlackboardArtifact > getArtifacts()
BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName, BlackboardArtifact.Category category)
Score deleteAnalysisResult(AnalysisResult analysisResult)
void postArtifacts(Collection< BlackboardArtifact > artifacts, String moduleName, Long ingestJobId)
List< AnalysisResult > getAnalysisResults(long sourceObjId)
Collection< BlackboardArtifact > getArtifacts(BlackboardArtifact.Type artifactType)
List< BlackboardArtifact > getExactMatchKeywordSearchResults(String keyword, TskData.KeywordSearchQueryType searchType, String kwsListName, Long dataSourceId)
List< DataArtifact > getDataArtifacts(int artifactTypeID)
List< AnalysisResult > getAnalysisResults(long dataSourceObjId, Integer artifactTypeID)
AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, long objId, Long dataSourceObjId, Score score, String conclusion, String configuration, String justification, Collection< BlackboardAttribute > attributesList, CaseDbTransaction transaction)
List< AnalysisResult > getAnalysisResultsWhere(String whereClause)
List< BlackboardArtifact > getArtifacts(int artifactTypeID, long dataSourceObjId)
synchronized BlackboardAttribute.Type getOrAddAttributeType(String typeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName)
Score deleteAnalysisResult(long artifactObjId, CaseDbTransaction transaction)
TimelineManager getTimelineManager()
void releaseSingleUserCaseReadLock()
static final Type TSK_KEYWORD
boolean artifactExists(Content content, BlackboardArtifact.Type artifactType, Collection< BlackboardAttribute > attributes)
static final Type TSK_KEYWORD_REGEXP
void updateFileAttributes(long fileObjId, List< Attribute > attributes)
BlackboardArtifact.Type getArtifactType(String artTypeName)
List< AnalysisResult > getAnalysisResults(long sourceObjId, int artifactTypeId)
void acquireSingleUserCaseWriteLock()
OsAccountInstance newOsAccountInstance(OsAccount osAccount, DataSource dataSource, OsAccountInstance.OsAccountInstanceType instanceType)
void postArtifact(BlackboardArtifact artifact, String moduleName, Long ingestJobId)
void releaseSingleUserCaseWriteLock()
List< BlackboardArtifact > getArtifacts(BlackboardArtifact.Type artifactType, BlackboardAttribute.Type attributeType, String value, Long dataSourceObjId, boolean showRejected)
static TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE fromType(long typeId)
DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long sourceObjId, Long dataSourceObjId, Collection< BlackboardAttribute > attributes, Long osAccountObjId, final CaseDbTransaction transaction)
DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long sourceObjId, Long dataSourceObjId, Collection< BlackboardAttribute > attributes, Long osAccountObjId, OsAccountInstance.OsAccountInstanceType osAccountInstanceType, final CaseDbTransaction transaction)
List< AnalysisResult > getAnalysisResultsByType(int artifactTypeId, long dataSourceObjId)
Collection< BlackboardArtifact.Type > getArtifactTypes()
List< DataArtifact > getDataArtifactsWhere(String whereClause)
AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, long objId, Long dataSourceObjId, Score score, String conclusion, String configuration, String justification, Collection< BlackboardAttribute > attributesList)
BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName)
boolean hasDataArtifacts(long sourceObjId)
List< BlackboardArtifact > getKeywordSearchResults(String keyword, String regex, TskData.KeywordSearchQueryType searchType, String kwsListName, Long dataSourceId)
static final Type TSK_SET_NAME
OsAccountManager getOsAccountManager()
void acquireSingleUserCaseReadLock()
long getArtifactsCount(int artifactTypeID, long dataSourceObjId)
BlackboardAttribute.Type getAttributeType(String attrTypeName)
Optional< Long > getIngestJobId()
List< BlackboardArtifact.Type > getArtifactTypesInUse(long dataSourceObjId)
boolean artifactExists(Content content, BlackboardArtifact.ARTIFACT_TYPE artifactType, Collection< BlackboardAttribute > attributes)
static ReviewStatus withID(int id)
BlackboardArtifact.Type getArtifactType(int artTypeId)
long getArtifactsCount(int artifactTypeID)
List< AnalysisResult > getAnalysisResultsByType(int artifactTypeId)
ScoringManager getScoringManager()