Sleuth Kit Java Bindings (JNI)  4.12.1
Java bindings for using The Sleuth Kit
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  */
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;
45 import static org.sleuthkit.datamodel.SleuthkitCase.closeConnection;
46 import static org.sleuthkit.datamodel.SleuthkitCase.closeResultSet;
47 import static org.sleuthkit.datamodel.SleuthkitCase.closeStatement;
48 
53 public 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  Statement s = null;
209  ResultSet rs = null;
210  CaseDbTransaction trans = null;
211  try {
212  trans = caseDb.beginTransaction();
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
230  BlackboardArtifact.Type type = new BlackboardArtifact.Type(maxID, typeName, displayName, category);
231  this.typeIdToArtifactTypeMap.put(type.getTypeID(), type);
232  this.typeNameToArtifactTypeMap.put(type.getTypeName(), type);
233  trans.commit();
234  trans = null;
235  return type;
236  } else {
237  trans.commit();
238  trans = null;
239  try {
240  return getArtifactType(typeName);
241  } catch (TskCoreException ex) {
242  throw new BlackboardException("Failed to get or add artifact type: " + typeName, ex);
243  }
244  }
245  } catch (SQLException | TskCoreException ex) {
246  try {
247  if (trans != null) {
248  trans.rollback();
249  trans = null;
250  }
251  } catch (TskCoreException ex2) {
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 {
260  trans.rollback();
261  } catch (TskCoreException ex) {
262  throw new BlackboardException("Error rolling back transaction", ex);
263  }
264  }
265  }
266  }
267 
278  public BlackboardAttribute.Type getAttributeType(String attrTypeName) throws TskCoreException {
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
290  BlackboardAttribute.Type type = null;
291  if (rs.next()) {
292  type = new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"),
293  rs.getString("display_name"), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type")));
294  this.typeIdToAttributeTypeMap.put(type.getTypeID(), type);
295  this.typeNameToAttributeTypeMap.put(attrTypeName, type);
296  }
297  return type;
298  } catch (SQLException ex) {
299  throw new TskCoreException("Error getting attribute type id", 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 
358  public BlackboardArtifact.Type getArtifactType(String artTypeName) throws TskCoreException {
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
370  BlackboardArtifact.Type type = null;
371  if (rs.next()) {
372  type = new BlackboardArtifact.Type(rs.getInt("artifact_type_id"),
373  rs.getString("type_name"), rs.getString("display_name"),
374  BlackboardArtifact.Category.fromID(rs.getInt("category_type")));
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
412  BlackboardArtifact.Type type = null;
413  if (rs.next()) {
414  type = new BlackboardArtifact.Type(rs.getInt("artifact_type_id"),
415  rs.getString("type_name"), rs.getString("display_name"),
416  BlackboardArtifact.Category.fromID(rs.getInt("category_type")));
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 
442  public ArrayList<BlackboardAttribute> getBlackboardAttributes(final BlackboardArtifact artifact) throws TskCoreException {
443  CaseDbConnection connection = null;
444  Statement statement = null;
445  ResultSet rs = null;
446 
447  String rowId;
448  switch (caseDb.getDatabaseType()) {
449  case POSTGRESQL:
450  rowId = "attrs.CTID";
451  break;
452  case SQLITE:
453  rowId = "attrs.ROWID";
454  break;
455  default:
456  throw new TskCoreException("Unknown database type: " + caseDb.getDatabaseType());
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()) {
474  final BlackboardAttribute attr = createAttributeFromResultSet(rs);
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;
519  switch (caseDb.getDatabaseType()) {
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
618  public void updateFileAttributes(long fileObjId, List<Attribute> attributes) throws TskCoreException {
619 
621  try (CaseDbConnection connection = caseDb.getConnection()) {
622  for (Attribute attr : attributes) {
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 
630  if (attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE) {
631  preparedStatement.setBytes(1, attr.getValueBytes());
632  } else {
633  preparedStatement.setBytes(1, null);
634  }
635 
636  if (attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING
637  || attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
638  preparedStatement.setString(2, attr.getValueString());
639  } else {
640  preparedStatement.setString(2, null);
641  }
642 
643  if (attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.INTEGER) {
644  preparedStatement.setInt(3, attr.getValueInt());
645  } else {
646  preparedStatement.setNull(3, java.sql.Types.INTEGER);
647  }
648 
649  if (attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME
650  || attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.LONG) {
651  preparedStatement.setLong(4, attr.getValueLong());
652  } else {
653  preparedStatement.setNull(4, java.sql.Types.BIGINT);
654  }
655 
656  if (attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE) {
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 
682  ArrayList<Attribute> getFileAttributes(final AbstractFile file) throws TskCoreException {
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");
705  BlackboardAttribute.Type attributeType;
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  }
781  if (caseDb.getDatabaseType() == TskData.DbType.POSTGRESQL) {
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  }
789  if (caseDb.getDatabaseType() == TskData.DbType.POSTGRESQL) {
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  }
840  if (caseDb.getDatabaseType() == TskData.DbType.POSTGRESQL) {
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  }
848  if (caseDb.getDatabaseType() == TskData.DbType.POSTGRESQL) {
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 
879  public AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, long objId, Long dataSourceObjId, Score score,
880  String conclusion, String configuration, String justification, Collection<BlackboardAttribute> attributesList)
882 
883  if (artifactType.getCategory() != BlackboardArtifact.Category.ANALYSIS_RESULT) {
884  throw new BlackboardException(String.format("Artifact type (name = %s) is not of Analysis Result category. ", artifactType.getTypeName()));
885  }
886 
887  CaseDbTransaction transaction = caseDb.beginTransaction();
888  try {
889  AnalysisResultAdded analysisResult = newAnalysisResult(artifactType, objId, dataSourceObjId, score,
890  conclusion, configuration, justification, attributesList, transaction);
891  transaction.commit();
892  return analysisResult;
893  } catch (TskCoreException | BlackboardException ex) {
894  try {
895  transaction.rollback();
896  } catch (TskCoreException ex2) {
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 
928  public AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, long objId, Long dataSourceObjId, Score score,
929  String conclusion, String configuration, String justification, Collection<BlackboardAttribute> attributesList, CaseDbTransaction transaction) throws BlackboardException {
930 
931  if (artifactType.getCategory() != BlackboardArtifact.Category.ANALYSIS_RESULT) {
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()) {
941  analysisResult.addAttributes(attributesList, transaction);
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.
948  return new AnalysisResultAdded(analysisResult, aggregateScore);
949 
950  } catch (TskCoreException ex) {
951  throw new BlackboardException("Failed to add analysis result.", ex);
952  }
953  }
954 
970 
971  CaseDbTransaction transaction = this.caseDb.beginTransaction();
972  try {
973  Score score = deleteAnalysisResult(analysisResult, transaction);
974  transaction.commit();
975  transaction = null;
976 
977  return score;
978  } finally {
979  if (transaction != null) {
980  transaction.rollback();
981  }
982  }
983  }
984 
998  public Score deleteAnalysisResult(long artifactObjId, CaseDbTransaction transaction) throws TskCoreException {
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 
1006  return deleteAnalysisResult(analysisResults.get(0), transaction);
1007  }
1008 
1022  private Score deleteAnalysisResult(AnalysisResult analysisResult, CaseDbTransaction transaction) throws TskCoreException {
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 
1079  public List<AnalysisResult> getAnalysisResultsByType(int artifactTypeId) throws TskCoreException {
1080  return getAnalysisResultsWhere(" artifacts.artifact_type_id = " + artifactTypeId);
1081  }
1082 
1094  public List<AnalysisResult> getAnalysisResultsByType(int artifactTypeId, long dataSourceObjId) throws TskCoreException {
1095  return getAnalysisResultsWhere(" artifacts.artifact_type_id = " + artifactTypeId + " AND artifacts.data_source_obj_id = " + dataSourceObjId);
1096  }
1097 
1111  public List<AnalysisResult> getAnalysisResults(long dataSourceObjId, Integer artifactTypeID) throws TskCoreException {
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  }
1118  return getAnalysisResultsWhere(whereClause, connection);
1119  } finally {
1121  }
1122  }
1123 
1134  public List<AnalysisResult> getAnalysisResults(long sourceObjId) throws TskCoreException {
1135  return getAnalysisResultsWhere(" artifacts.obj_id = " + sourceObjId);
1136  }
1137 
1148  List<DataArtifact> getDataArtifactsBySource(long sourceObjId) throws TskCoreException {
1150  try (CaseDbConnection connection = caseDb.getConnection()) {
1151  return getDataArtifactsWhere(String.format(" artifacts.obj_id = %d", sourceObjId), connection);
1152  } finally {
1154  }
1155  }
1156 
1166  public boolean hasDataArtifacts(long sourceObjId) throws TskCoreException {
1167  return hasArtifactsOfCategory(BlackboardArtifact.Category.DATA_ARTIFACT, sourceObjId);
1168  }
1169 
1180  public boolean hasAnalysisResults(long sourceObjId) throws TskCoreException {
1181  return hasArtifactsOfCategory(BlackboardArtifact.Category.ANALYSIS_RESULT, sourceObjId);
1182  }
1183 
1196  private boolean hasArtifactsOfCategory(BlackboardArtifact.Category category, long sourceObjId) throws TskCoreException {
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 
1205  try (SleuthkitCase.CaseDbConnection connection = caseDb.getConnection();
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 {
1232  return getAnalysisResultsWhere(" artifacts.obj_id = " + sourceObjId, connection);
1233  }
1234 
1246  public List<AnalysisResult> getAnalysisResults(long sourceObjId, int artifactTypeId) throws TskCoreException {
1247  // Get the artifact type to check that it in the analysis result category.
1248  BlackboardArtifact.Type artifactType = getArtifactType(artifactTypeId);
1249  if (artifactType.getCategory() != BlackboardArtifact.Category.ANALYSIS_RESULT) {
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;
1255  return getAnalysisResultsWhere(whereClause);
1256  }
1257 
1269  public List<AnalysisResult> getAnalysisResultsWhere(String whereClause) throws TskCoreException {
1271  try (CaseDbConnection connection = caseDb.getConnection()) {
1272  return getAnalysisResultsWhere(whereClause, connection);
1273  } finally {
1275  }
1276  }
1277 
1290  List<AnalysisResult> getAnalysisResultsWhere(String whereClause, CaseDbConnection connection) throws TskCoreException {
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 
1314  public AnalysisResult getAnalysisResultById(long artifactObjId) throws TskCoreException {
1315 
1316  String whereClause = " artifacts.artifact_obj_id = " + artifactObjId;
1317  List<AnalysisResult> results = getAnalysisResultsWhere(whereClause);
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"),
1352  BlackboardArtifact.ReviewStatus.withID(resultSet.getInt("review_status_id")),
1353  new Score(Score.Significance.fromID(resultSet.getInt("significance")), Score.Priority.fromID(resultSet.getInt("priority"))),
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 
1394  public List<DataArtifact> getDataArtifacts(long dataSourceObjId, Integer artifactTypeID) throws TskCoreException {
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  }
1401  return getDataArtifactsWhere(whereClause, connection);
1402  } finally {
1404  }
1405  }
1406 
1418  public List<DataArtifact> getDataArtifacts(int artifactTypeID, long dataSourceObjId) throws TskCoreException {
1419 
1420  // Get the artifact type to check that it in the data artifact category.
1421  BlackboardArtifact.Type artifactType = getArtifactType(artifactTypeID);
1422  if (artifactType.getCategory() != BlackboardArtifact.Category.DATA_ARTIFACT) {
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 
1431  return getDataArtifactsWhere(whereClause, connection);
1432  } finally {
1434  }
1435  }
1436 
1447  public List<DataArtifact> getDataArtifacts(int artifactTypeID) throws TskCoreException {
1448  // Get the artifact type to check that it in the data artifact category.
1449  BlackboardArtifact.Type artifactType = getArtifactType(artifactTypeID);
1450  if (artifactType.getCategory() != BlackboardArtifact.Category.DATA_ARTIFACT) {
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 
1458  return getDataArtifactsWhere(whereClause, connection);
1459  } finally {
1461  }
1462  }
1463 
1474  public DataArtifact getDataArtifactById(long artifactObjId) throws TskCoreException {
1476  try (CaseDbConnection connection = caseDb.getConnection()) {
1477  String whereClause = " artifacts.artifact_obj_id = " + artifactObjId;
1478 
1479  List<DataArtifact> artifacts = getDataArtifactsWhere(whereClause, connection);
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 
1503  public List<DataArtifact> getDataArtifactsWhere(String whereClause) throws TskCoreException {
1505  try (CaseDbConnection connection = caseDb.getConnection()) {
1506  return getDataArtifactsWhere(whereClause, connection);
1507  } finally {
1509  }
1510  }
1511 
1524  List<DataArtifact> getDataArtifactsWhere(String whereClause, CaseDbConnection connection) throws TskCoreException {
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 
1591  public synchronized BlackboardAttribute.Type getOrAddAttributeType(String typeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName) throws BlackboardException {
1592  // check local cache
1593  if (typeNameToAttributeTypeMap.containsKey(typeName)) {
1594  return typeNameToAttributeTypeMap.get(typeName);
1595  }
1596 
1597  CaseDbTransaction trans = null;
1598  try {
1599  trans = this.caseDb.beginTransaction();
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()) {
1609  trans.commit();
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 
1656  trans.commit();
1657  trans = null;
1658 
1659  BlackboardAttribute.Type type = new BlackboardAttribute.Type(attrId, typeName, displayName, valueType);
1660  this.typeIdToAttributeTypeMap.put(type.getTypeID(), type);
1661  this.typeNameToAttributeTypeMap.put(type.getTypeName(), type);
1662  return type;
1663  } catch (SQLException | TskCoreException ex) {
1664  throw new BlackboardException("Error adding attribute type: " + typeName, ex);
1665  } finally {
1666  try {
1667  if (trans != null) {
1668  trans.rollback();
1669  trans = null;
1670  }
1671  } catch (TskCoreException ex2) {
1672  LOGGER.log(Level.SEVERE, "Error rolling back transaction", ex2);
1673  }
1674  }
1675  }
1676 
1688  public List<BlackboardArtifact.Type> getArtifactTypesInUse(long dataSourceObjId) throws TskCoreException {
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 
1700  try (SleuthkitCase.CaseDbConnection connection = caseDb.getConnection();
1701  Statement statement = connection.createStatement();
1702  ResultSet resultSet = connection.executeQuery(statement, queryString);) {
1703 
1704  List<BlackboardArtifact.Type> uniqueArtifactTypes = new ArrayList<>();
1705  while (resultSet.next()) {
1706  uniqueArtifactTypes.add(new BlackboardArtifact.Type(resultSet.getInt("artifact_type_id"),
1707  resultSet.getString("type_name"), resultSet.getString("display_name"),
1708  BlackboardArtifact.Category.fromID(resultSet.getInt("category_type"))));
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 
1730  public long getArtifactsCount(int artifactTypeID, long dataSourceObjId) throws TskCoreException {
1731  return getArtifactsCountHelper(artifactTypeID,
1732  "blackboard_artifacts.data_source_obj_id = '" + dataSourceObjId + "';");
1733  }
1734 
1746  public long getArtifactsCount(int artifactTypeID) throws TskCoreException {
1747  return getArtifactsCountHelper(artifactTypeID, null);
1748  }
1749 
1762  public List<BlackboardArtifact> getArtifacts(int artifactTypeID, long dataSourceObjId) throws TskCoreException {
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,
1780  Collection<Long> dataSourceObjIds) throws TskCoreException {
1781 
1782  if (artifactTypes.isEmpty() || dataSourceObjIds.isEmpty()) {
1783  return new ArrayList<>();
1784  }
1785 
1786  String analysisResultQuery = "";
1787  String dataArtifactQuery = "";
1788 
1789  for (BlackboardArtifact.Type type : artifactTypes) {
1790  if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
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 + ") ";
1815  artifacts.addAll(this.getAnalysisResultsWhere(fullQuery));
1816  }
1817 
1818  if (!dataArtifactQuery.isEmpty()) {
1819  String fullQuery = "( " + dataArtifactQuery + " ) AND (" + dsQuery + ") ";
1820  artifacts.addAll(this.getDataArtifactsWhere(fullQuery));
1821  }
1822 
1823  return artifacts;
1824  }
1825 
1842  public List<BlackboardArtifact> getArtifacts(BlackboardArtifact.Type artifactType,
1843  BlackboardAttribute.Type attributeType, String value, Long dataSourceObjId,
1844  boolean showRejected) throws TskCoreException {
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
1849  + (showRejected ? "" : " AND artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID()) //NON-NLS
1850  + (dataSourceObjId != null ? " AND artifacts.data_source_obj_id = " + dataSourceObjId : ""); //NON-NLS
1851 
1852  List<BlackboardArtifact> artifacts = new ArrayList<>();
1854 
1855  String finalQuery = (artifactType.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT
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 
1863  if (artifactType.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
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 
1898  public List<BlackboardArtifact> getExactMatchKeywordSearchResults(String keyword, TskData.KeywordSearchQueryType searchType, String kwsListName, Long dataSourceId) throws TskCoreException {
1899  return getKeywordSearchResults(keyword, "", searchType, kwsListName, dataSourceId);
1900  }
1901 
1927  public List<BlackboardArtifact> getKeywordSearchResults(String keyword, String regex, TskData.KeywordSearchQueryType searchType, String kwsListName, Long dataSourceId) throws TskCoreException {
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 = "
1965  + BlackboardAttribute.Type.TSK_SET_NAME.getTypeID() + " LIMIT 1) AS set_name, "
1966  + " (SELECT value_int32 FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = "
1967  + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID() + " LIMIT 1) AS search_type, "
1968  + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = "
1969  + BlackboardAttribute.Type.TSK_KEYWORD_REGEXP.getTypeID() + " LIMIT 1) AS regexp_str, "
1970  + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = "
1971  + BlackboardAttribute.Type.TSK_KEYWORD.getTypeID() + " LIMIT 1) AS keyword "
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 "
1977  + " WHERE types.category_type = " + BlackboardArtifact.Category.ANALYSIS_RESULT.getID()
1978  + " AND artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() + " "
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
2042  + " AND blackboard_artifacts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID();
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 
2077  public boolean artifactExists(Content content, BlackboardArtifact.Type artifactType, Collection<BlackboardAttribute> attributes) throws TskCoreException {
2078  List<BlackboardArtifact> existingArtifacts = content.getArtifacts(artifactType.getTypeID());
2079  for (BlackboardArtifact artifact : existingArtifacts) {
2080  if (attributesMatch(artifact.getAttributes(), attributes)) {
2081  return true;
2082  }
2083  }
2084  return false;
2085  }
2086 
2102  @Deprecated
2103  public boolean artifactExists(Content content, BlackboardArtifact.ARTIFACT_TYPE artifactType, Collection<BlackboardAttribute> attributes) throws TskCoreException {
2104  return artifactExists(content, getArtifactType(artifactType.getTypeID()), attributes);
2105  }
2106 
2116  private boolean attributesMatch(Collection<BlackboardAttribute> fileAttributesList, Collection<BlackboardAttribute> expectedAttributesList) {
2117  for (BlackboardAttribute expectedAttribute : expectedAttributesList) {
2118  boolean match = false;
2119  for (BlackboardAttribute fileAttribute : fileAttributesList) {
2120  BlackboardAttribute.Type attributeType = fileAttribute.getAttributeType();
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 
2230  public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long sourceObjId, Long dataSourceObjId,
2231  Collection<BlackboardAttribute> attributes, Long osAccountId) throws TskCoreException {
2232 
2233  if (artifactType.getCategory() != BlackboardArtifact.Category.DATA_ARTIFACT) {
2234  throw new TskCoreException(String.format("Artifact type (name = %s) is not of Data Artifact category. ", artifactType.getTypeName()));
2235  }
2236 
2237  CaseDbTransaction transaction = caseDb.beginTransaction();
2238  try {
2239  DataArtifact dataArtifact = newDataArtifact(artifactType, sourceObjId, dataSourceObjId,
2240  attributes, osAccountId, transaction);
2241  transaction.commit();
2242  return dataArtifact;
2243  } catch (TskCoreException ex) {
2244  try {
2245  transaction.rollback();
2246  } catch (TskCoreException ex2) {
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 
2275  public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long sourceObjId, Long dataSourceObjId,
2276  Collection<BlackboardAttribute> attributes, Long osAccountObjId, final CaseDbTransaction transaction) throws TskCoreException {
2277 
2278  return newDataArtifact(artifactType, sourceObjId, dataSourceObjId,
2279  attributes, osAccountObjId, OsAccountInstance.OsAccountInstanceType.ACCESSED, transaction);
2280  }
2281 
2304  public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long sourceObjId, Long dataSourceObjId,
2305  Collection<BlackboardAttribute> attributes,
2306  Long osAccountObjId, OsAccountInstance.OsAccountInstanceType osAccountInstanceType,
2307  final CaseDbTransaction transaction) throws TskCoreException {
2308 
2309  if (artifactType.getCategory() != BlackboardArtifact.Category.DATA_ARTIFACT) {
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();
2315  long artifact_obj_id = caseDb.addObject(sourceObjId, TskData.ObjectType.ARTIFACT.getObjectType(), connection);
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();
2321  DataArtifact dataArtifact = new DataArtifact(caseDb, resultSet.getLong(1), //last_insert_rowid()
2322  sourceObjId, artifact_obj_id, dataSourceObjId, artifactType.getTypeID(),
2323  artifactType.getTypeName(), artifactType.getDisplayName(), BlackboardArtifact.ReviewStatus.UNDECIDED,
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)) {
2339  caseDb.getOsAccountManager().newOsAccountInstance(osAccountObjId, dataSourceObjId, osAccountInstanceType, connection);
2340  }
2341  }
2342 
2343  // if attributes are provided, add them to the artifact.
2344  if (Objects.nonNull(attributes) && !attributes.isEmpty()) {
2345  dataArtifact.addAttributes(attributes, transaction);
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 
2366  List<BlackboardArtifact> getArtifactsBySourceId(BlackboardArtifact.Type artifactType, long sourceObjId) throws TskCoreException {
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) {
2383  artifacts.addAll(getAnalysisResultsByType(artifactType.getTypeID()));
2384  } else {
2385  artifacts.addAll(getDataArtifacts(artifactType.getTypeID()));
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) {
2412  artifacts.addAll(getAnalysisResultsWhere(whereWithType));
2413  } else {
2414  artifacts.addAll(getDataArtifactsWhere(whereWithType));
2415  }
2416 
2417  return artifacts;
2418  }
2419 
2425  final public class ArtifactsPostedEvent {
2426 
2427  private final String moduleName;
2428  private final ImmutableSet<BlackboardArtifact.Type> artifactTypes;
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());
2447  Set<BlackboardArtifact.Type> types = new HashSet<>();
2448  for (Integer typeID : typeIDS) {
2449  try {
2450  types.add(getArtifactType(typeID));
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 
2466  public Collection<BlackboardArtifact> getArtifacts() {
2467  return ImmutableSet.copyOf(artifacts);
2468  }
2469 
2477  public Collection<BlackboardArtifact> getArtifacts(BlackboardArtifact.Type artifactType) {
2478  Set<BlackboardArtifact> tempSet = artifacts.stream()
2479  .filter(artifact -> artifact.getArtifactTypeID() == artifactType.getTypeID())
2480  .collect(Collectors.toSet());
2481  return ImmutableSet.copyOf(tempSet);
2482  }
2483 
2489  public String getModuleName() {
2490  return moduleName;
2491  }
2492 
2499  return ImmutableSet.copyOf(artifactTypes);
2500  }
2501 
2508  public Optional<Long> getIngestJobId() {
2509  return Optional.ofNullable(ingestJobId);
2510  }
2511 
2512  }
2513 }
static Priority fromID(int id)
Definition: Score.java:184
List< BlackboardArtifact > getArtifacts(Collection< BlackboardArtifact.Type > artifactTypes, Collection< Long > dataSourceObjIds)
static Significance fromID(int id)
Definition: Score.java:118
AnalysisResult getAnalysisResultById(long artifactObjId)
void postArtifact(BlackboardArtifact artifact, String moduleName)
Definition: Blackboard.java:99
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)
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)
boolean artifactExists(Content content, BlackboardArtifact.Type artifactType, Collection< BlackboardAttribute > attributes)
void updateFileAttributes(long fileObjId, List< Attribute > attributes)
BlackboardArtifact.Type getArtifactType(String artTypeName)
List< AnalysisResult > getAnalysisResults(long sourceObjId, int artifactTypeId)
OsAccountInstance newOsAccountInstance(OsAccount osAccount, DataSource dataSource, OsAccountInstance.OsAccountInstanceType instanceType)
void postArtifact(BlackboardArtifact artifact, String moduleName, Long ingestJobId)
List< BlackboardArtifact > getArtifacts(BlackboardArtifact.Type artifactType, BlackboardAttribute.Type attributeType, String value, Long dataSourceObjId, boolean showRejected)
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)
long getArtifactsCount(int artifactTypeID, long dataSourceObjId)
BlackboardAttribute.Type getAttributeType(String attrTypeName)
List< BlackboardArtifact.Type > getArtifactTypesInUse(long dataSourceObjId)
boolean artifactExists(Content content, BlackboardArtifact.ARTIFACT_TYPE artifactType, Collection< BlackboardAttribute > attributes)
BlackboardArtifact.Type getArtifactType(int artTypeId)
long getArtifactsCount(int artifactTypeID)
List< AnalysisResult > getAnalysisResultsByType(int artifactTypeId)

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.