Sleuth Kit Java Bindings (JNI)  4.11.1
Java bindings for using The Sleuth Kit
SleuthkitCase.java
Go to the documentation of this file.
1 /*
2  * Sleuth Kit Data Model
3  *
4  * Copyright 2011-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.cache.Cache;
23 import com.google.common.cache.CacheBuilder;
24 import com.google.common.collect.ImmutableSet;
25 import com.google.common.eventbus.EventBus;
26 import com.mchange.v2.c3p0.ComboPooledDataSource;
27 import com.mchange.v2.c3p0.DataSources;
28 import com.mchange.v2.c3p0.PooledDataSource;
29 import com.zaxxer.sparsebits.SparseBitSet;
30 import java.beans.PropertyVetoException;
31 import java.io.BufferedInputStream;
32 import java.io.BufferedOutputStream;
33 import java.io.File;
34 import java.io.FileInputStream;
35 import java.io.FileOutputStream;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.io.OutputStream;
39 import java.io.UnsupportedEncodingException;
40 import java.net.InetAddress;
41 import java.net.URLEncoder;
42 import java.nio.charset.StandardCharsets;
43 import java.nio.file.Paths;
44 import java.sql.Connection;
45 import java.sql.DriverManager;
46 import java.sql.PreparedStatement;
47 import java.sql.ResultSet;
48 import java.sql.SQLException;
49 import java.sql.Statement;
50 import java.text.SimpleDateFormat;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.Collection;
54 import java.util.Collections;
55 import java.util.concurrent.atomic.AtomicBoolean;
56 import java.util.concurrent.atomic.AtomicInteger;
57 import java.util.Date;
58 import java.util.EnumMap;
59 import java.util.HashMap;
60 import java.util.HashSet;
61 import java.util.LinkedHashMap;
62 import java.util.List;
63 import java.util.Map;
64 import java.util.MissingResourceException;
65 import java.util.Objects;
66 import java.util.Properties;
67 import java.util.ResourceBundle;
68 import java.util.Set;
69 import java.util.UUID;
70 import java.util.concurrent.TimeUnit;
71 import java.util.concurrent.locks.ReentrantReadWriteLock;
72 import java.util.logging.Level;
73 import java.util.logging.Logger;
74 import java.util.stream.Collectors;
75 import org.apache.commons.lang3.StringUtils;
76 import org.postgresql.util.PSQLState;
94 import org.sqlite.SQLiteConfig;
95 import org.sqlite.SQLiteDataSource;
96 import org.sqlite.SQLiteJDBCLoader;
97 
102 public class SleuthkitCase {
103 
104  private static final int MAX_DB_NAME_LEN_BEFORE_TIMESTAMP = 47;
105 
106  static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION
107  = new CaseDbSchemaVersionNumber(9, 3);
108 
109  private static final long BASE_ARTIFACT_ID = Long.MIN_VALUE; // Artifact ids will start at the lowest negative value
110  private static final Logger logger = Logger.getLogger(SleuthkitCase.class.getName());
111  private static final ResourceBundle bundle = ResourceBundle.getBundle("org.sleuthkit.datamodel.Bundle");
112  private static final int IS_REACHABLE_TIMEOUT_MS = 1000;
113  private static final String SQL_ERROR_CONNECTION_GROUP = "08";
114  private static final String SQL_ERROR_AUTHENTICATION_GROUP = "28";
115  private static final String SQL_ERROR_PRIVILEGE_GROUP = "42";
116  private static final String SQL_ERROR_RESOURCE_GROUP = "53";
117  private static final String SQL_ERROR_LIMIT_GROUP = "54";
118  private static final String SQL_ERROR_INTERNAL_GROUP = "xx";
119 
120  private static final Set<String> CORE_TABLE_NAMES = ImmutableSet.of(
121  "tsk_events",
122  "tsk_event_descriptions",
123  "tsk_event_types",
124  "tsk_db_info",
125  "tsk_objects",
126  "tsk_image_info",
127  "tsk_image_names",
128  "tsk_vs_info",
129  "tsk_vs_parts",
130  "tsk_fs_info",
131  "tsk_file_layout",
132  "tsk_files",
133  "tsk_files_path",
134  "tsk_files_derived",
135  "tsk_files_derived_method",
136  "tag_names",
137  "content_tags",
138  "blackboard_artifact_tags",
139  "blackboard_artifacts",
140  "blackboard_attributes",
141  "blackboard_artifact_types",
142  "blackboard_attribute_types",
143  "data_source_info",
144  "file_encoding_types",
145  "ingest_module_types",
146  "ingest_job_status_types",
147  "ingest_modules",
148  "ingest_jobs",
149  "ingest_job_modules",
150  "account_types",
151  "accounts",
152  "account_relationships",
153  "review_statuses",
154  "reports,");
155 
156  private static final Set<String> CORE_INDEX_NAMES = ImmutableSet.of(
157  "parObjId",
158  "layout_objID",
159  "artifact_objID",
160  "artifact_artifact_objID",
161  "artifact_typeID",
162  "attrsArtifactID",
163  "mime_type",
164  "file_extension",
165  "relationships_account1",
166  "relationships_account2",
167  "relationships_relationship_source_obj_id",
168  "relationships_date_time",
169  "relationships_relationship_type",
170  "relationships_data_source_obj_id",
171  "events_time",
172  "events_type",
173  "events_data_source_obj_id",
174  "events_file_obj_id",
175  "events_artifact_id");
176 
177  private static final String TSK_VERSION_KEY = "TSK_VER";
178  private static final String SCHEMA_MAJOR_VERSION_KEY = "SCHEMA_MAJOR_VERSION";
179  private static final String SCHEMA_MINOR_VERSION_KEY = "SCHEMA_MINOR_VERSION";
180  private static final String CREATION_SCHEMA_MAJOR_VERSION_KEY = "CREATION_SCHEMA_MAJOR_VERSION";
181  private static final String CREATION_SCHEMA_MINOR_VERSION_KEY = "CREATION_SCHEMA_MINOR_VERSION";
182 
183  private final ConnectionPool connections;
184  private final Object carvedFileDirsLock = new Object();
185  private final static int MAX_CARVED_FILES_PER_FOLDER = 2000;
186  private final Map<Long, CarvedFileDirInfo> rootIdsToCarvedFileDirs = new HashMap<>();
187  private final Map<Long, FileSystem> fileSystemIdMap = new HashMap<>(); // Cache for file system files.
188  private final List<ErrorObserver> sleuthkitCaseErrorObservers = new ArrayList<>();
189  private final String databaseName;
190  private final String dbPath;
191  private final DbType dbType;
192  private final String caseDirPath;
193  private SleuthkitJNI.CaseDbHandle caseHandle;
194  private final String caseHandleIdentifier; // Used to identify this case in the JNI cache.
195  private String dbBackupPath;
196  private AtomicBoolean timelineEventsDisabled = new AtomicBoolean(false);
197 
198  private CaseDbSchemaVersionNumber caseDBSchemaCreationVersion;
199 
200  // Objects for caching the result of isRootDirectory(). Lock is for visibility only.
201  private final Object rootDirectoryMapLock = new Object();
202  private final Map<RootDirectoryKey, Long> rootDirectoryMap = new HashMap<>();
203  private final Cache<Long, Boolean> isRootDirectoryCache
204  = CacheBuilder.newBuilder().maximumSize(200000).expireAfterAccess(5, TimeUnit.MINUTES).build();
205 
206  /*
207  * First parameter is used to specify the SparseBitSet to use, as object IDs
208  * can be larger than the max size of a SparseBitSet
209  */
210  private final Map<Long, SparseBitSet> hasChildrenBitSetMap = new HashMap<>();
211 
212  private long nextArtifactId; // Used to ensure artifact ids come from the desired range.
213  // This read/write lock is used to implement a layer of locking on top of
214  // the locking protocol provided by the underlying SQLite database. The Java
215  // locking protocol improves performance for reasons that are not currently
216  // understood. Note that the lock is contructed to use a fairness policy.
217  private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true);
218 
219  private CommunicationsManager communicationsMgr;
220  private TimelineManager timelineMgr;
221  private Blackboard blackboard;
222  private CaseDbAccessManager dbAccessManager;
223  private FileManager fileManager;
224  private TaggingManager taggingMgr;
225  private ScoringManager scoringManager;
226  private OsAccountRealmManager osAccountRealmManager;
227  private OsAccountManager osAccountManager;
228  private HostManager hostManager;
229  private PersonManager personManager;
230  private HostAddressManager hostAddressManager;
231 
232  private final Map<String, Set<Long>> deviceIdToDatasourceObjIdMap = new HashMap<>();
233 
234  private final EventBus eventBus = new EventBus("SleuthkitCase-EventBus");
235 
236  public void registerForEvents(Object listener) {
237  eventBus.register(listener);
238  }
239 
240  public void unregisterForEvents(Object listener) {
241  eventBus.unregister(listener);
242  }
243 
244  void fireTSKEvent(Object event) {
245  eventBus.post(event);
246  }
247 
248  // Cache of frequently used content objects (e.g. data source, file system).
249  private final Map<Long, Content> frequentlyUsedContentMap = new HashMap<>();
250 
251  private Examiner cachedCurrentExaminer = null;
252 
253  static {
254  Properties p = new Properties(System.getProperties());
255  p.put("com.mchange.v2.log.MLog", "com.mchange.v2.log.FallbackMLog");
256  p.put("com.mchange.v2.log.FallbackMLog.DEFAULT_CUTOFF_LEVEL", "SEVERE");
257  System.setProperties(p);
258  }
259 
274  public static void tryConnect(CaseDbConnectionInfo info) throws TskCoreException {
275  // Check if we can talk to the database.
276  if (info.getHost() == null || info.getHost().isEmpty()) {
277  throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.MissingHostname")); //NON-NLS
278  } else if (info.getPort() == null || info.getPort().isEmpty()) {
279  throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.MissingPort")); //NON-NLS
280  } else if (info.getUserName() == null || info.getUserName().isEmpty()) {
281  throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.MissingUsername")); //NON-NLS
282  } else if (info.getPassword() == null || info.getPassword().isEmpty()) {
283  throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.MissingPassword")); //NON-NLS
284  }
285 
286  try {
287  Class.forName("org.postgresql.Driver"); //NON-NLS
288  Connection conn = DriverManager.getConnection("jdbc:postgresql://" + info.getHost() + ":" + info.getPort() + "/postgres", info.getUserName(), info.getPassword()); //NON-NLS
289  if (conn != null) {
290  conn.close();
291  }
292  } catch (SQLException ex) {
293  String result;
294  String sqlState = ex.getSQLState().toLowerCase();
295  if (sqlState.startsWith(SQL_ERROR_CONNECTION_GROUP)) {
296  try {
297  if (InetAddress.getByName(info.getHost()).isReachable(IS_REACHABLE_TIMEOUT_MS)) {
298  // if we can reach the host, then it's probably port problem
299  result = bundle.getString("DatabaseConnectionCheck.Port"); //NON-NLS
300  } else {
301  result = bundle.getString("DatabaseConnectionCheck.HostnameOrPort"); //NON-NLS
302  }
303  } catch (IOException | MissingResourceException any) {
304  // it may be anything
305  result = bundle.getString("DatabaseConnectionCheck.Everything"); //NON-NLS
306  }
307  } else if (sqlState.startsWith(SQL_ERROR_AUTHENTICATION_GROUP)) {
308  result = bundle.getString("DatabaseConnectionCheck.Authentication"); //NON-NLS
309  } else if (sqlState.startsWith(SQL_ERROR_PRIVILEGE_GROUP)) {
310  result = bundle.getString("DatabaseConnectionCheck.Access"); //NON-NLS
311  } else if (sqlState.startsWith(SQL_ERROR_RESOURCE_GROUP)) {
312  result = bundle.getString("DatabaseConnectionCheck.ServerDiskSpace"); //NON-NLS
313  } else if (sqlState.startsWith(SQL_ERROR_LIMIT_GROUP)) {
314  result = bundle.getString("DatabaseConnectionCheck.ServerRestart"); //NON-NLS
315  } else if (sqlState.startsWith(SQL_ERROR_INTERNAL_GROUP)) {
316  result = bundle.getString("DatabaseConnectionCheck.InternalServerIssue"); //NON-NLS
317  } else {
318  result = bundle.getString("DatabaseConnectionCheck.Connection"); //NON-NLS
319  }
320  throw new TskCoreException(result);
321  } catch (ClassNotFoundException ex) {
322  throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.Installation")); //NON-NLS
323  }
324  }
325 
337  private SleuthkitCase(String dbPath, SleuthkitJNI.CaseDbHandle caseHandle, DbType dbType) throws Exception {
338  Class.forName("org.sqlite.JDBC");
339  this.dbPath = dbPath;
340  this.dbType = dbType;
341  File dbFile = new File(dbPath);
342  this.caseDirPath = dbFile.getParentFile().getAbsolutePath();
343  this.databaseName = dbFile.getName();
344  this.connections = new SQLiteConnections(dbPath);
345  this.caseHandle = caseHandle;
346  this.caseHandleIdentifier = caseHandle.getCaseDbIdentifier();
347  init();
348  logSQLiteJDBCDriverInfo();
349  }
350 
368  private SleuthkitCase(String host, int port, String dbName, String userName, String password, SleuthkitJNI.CaseDbHandle caseHandle, String caseDirPath, DbType dbType) throws Exception {
369  this.dbPath = "";
370  this.databaseName = dbName;
371  this.dbType = dbType;
372  this.caseDirPath = caseDirPath;
373  this.connections = new PostgreSQLConnections(host, port, dbName, userName, password);
374  this.caseHandle = caseHandle;
375  this.caseHandleIdentifier = caseHandle.getCaseDbIdentifier();
376  init();
377  }
378 
379  private void init() throws Exception {
380  blackboard = new Blackboard(this);
381  updateDatabaseSchema(null);
382  try (CaseDbConnection connection = connections.getConnection()) {
383  blackboard.initBlackboardArtifactTypes(connection);
384  blackboard.initBlackboardAttributeTypes(connection);
385  initNextArtifactId(connection);
386  initIngestModuleTypes(connection);
387  initIngestStatusTypes(connection);
388  initReviewStatuses(connection);
389  initEncodingTypes(connection);
390  populateHasChildrenMap(connection);
391  updateExaminers(connection);
392  initDBSchemaCreationVersion(connection);
393  }
394 
395  fileManager = new FileManager(this);
396  communicationsMgr = new CommunicationsManager(this);
397  timelineMgr = new TimelineManager(this);
398  dbAccessManager = new CaseDbAccessManager(this);
399  taggingMgr = new TaggingManager(this);
400  scoringManager = new ScoringManager(this);
401  osAccountRealmManager = new OsAccountRealmManager(this);
402  osAccountManager = new OsAccountManager(this);
403  hostManager = new HostManager(this);
404  personManager = new PersonManager(this);
405  hostAddressManager = new HostAddressManager(this);
406  }
407 
413  static Set<String> getCoreTableNames() {
414  return Collections.unmodifiableSet(CORE_TABLE_NAMES);
415  }
416 
422  static Set<String> getCoreIndexNames() {
423  return Collections.unmodifiableSet(CORE_INDEX_NAMES);
424  }
425 
434  boolean getHasChildren(Content content) {
435  long objId = content.getId();
436  long mapIndex = objId / Integer.MAX_VALUE;
437  int mapValue = (int) (objId % Integer.MAX_VALUE);
438 
439  synchronized (hasChildrenBitSetMap) {
440  if (hasChildrenBitSetMap.containsKey(mapIndex)) {
441  return hasChildrenBitSetMap.get(mapIndex).get(mapValue);
442  }
443  return false;
444  }
445  }
446 
452  private void setHasChildren(Long objId) {
453  long mapIndex = objId / Integer.MAX_VALUE;
454  int mapValue = (int) (objId % Integer.MAX_VALUE);
455 
456  synchronized (hasChildrenBitSetMap) {
457  if (hasChildrenBitSetMap.containsKey(mapIndex)) {
458  hasChildrenBitSetMap.get(mapIndex).set(mapValue);
459  } else {
460  SparseBitSet bitSet = new SparseBitSet();
461  bitSet.set(mapValue);
462  hasChildrenBitSetMap.put(mapIndex, bitSet);
463  }
464  }
465  }
466 
475  return communicationsMgr;
476  }
477 
484  return blackboard;
485  }
486 
493  return fileManager;
494  }
495 
504  return timelineMgr;
505  }
506 
507  /*
508  * Gets the case database access manager for this case.
509  *
510  * @return The per case CaseDbAccessManager object.
511  *
512  * @throws org.sleuthkit.datamodel.TskCoreException
513  */
515  return dbAccessManager;
516  }
517 
523  public synchronized TaggingManager getTaggingManager() {
524  return taggingMgr;
525  }
526 
535  return scoringManager;
536  }
537 
546  return osAccountRealmManager;
547  }
548 
557  return osAccountManager;
558  }
559 
568  return hostManager;
569  }
570 
579  return personManager;
580  }
581 
590  return hostAddressManager;
591  }
592 
602  private void initNextArtifactId(CaseDbConnection connection) throws SQLException {
604  try (Statement statement = connection.createStatement()) {
605  ResultSet resultSet = connection.executeQuery(statement, "SELECT MAX(artifact_id) AS max_artifact_id FROM blackboard_artifacts"); //NON-NLS
606  resultSet.next();
607  nextArtifactId = resultSet.getLong("max_artifact_id") + 1;
608  if (nextArtifactId == 1) {
609  nextArtifactId = BASE_ARTIFACT_ID;
610  }
611  } finally {
613  }
614  }
615 
623  private void initIngestModuleTypes(CaseDbConnection connection) throws SQLException, TskCoreException {
624  Statement statement = null;
625  ResultSet resultSet = null;
627  try {
628  statement = connection.createStatement();
629  for (IngestModuleType type : IngestModuleType.values()) {
630  try {
631  String query = "INSERT INTO ingest_module_types (type_id, type_name) VALUES (" + type.ordinal() + ", '" + type.toString() + "')"; // NON-NLS
632  if (getDatabaseType().equals(DbType.POSTGRESQL)) {
633  query += " ON CONFLICT ON CONSTRAINT ingest_module_types_pkey DO NOTHING"; // NON-NLS
634  }
635  statement.execute(query);
636  } catch (SQLException ex) {
637  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) as count FROM ingest_module_types WHERE type_id = " + type.ordinal() + ";"); //NON-NLS
638  resultSet.next();
639  if (resultSet.getLong("count") == 0) {
640  throw ex;
641  }
642  resultSet.close();
643  resultSet = null;
644  }
645  }
646  } finally {
647  closeResultSet(resultSet);
648  closeStatement(statement);
650  }
651  }
652 
660  private void initIngestStatusTypes(CaseDbConnection connection) throws SQLException, TskCoreException {
661  Statement statement = null;
662  ResultSet resultSet = null;
664  try {
665  statement = connection.createStatement();
666  for (IngestJobStatusType type : IngestJobStatusType.values()) {
667  try {
668  String query = "INSERT INTO ingest_job_status_types (type_id, type_name) VALUES (" + type.ordinal() + ", '" + type.toString() + "')"; // NON-NLS
669  if (getDatabaseType().equals(DbType.POSTGRESQL)) {
670  query += " ON CONFLICT ON CONSTRAINT ingest_job_status_types_pkey DO NOTHING"; // NON-NLS
671  }
672  statement.execute(query);
673  } catch (SQLException ex) {
674  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) as count FROM ingest_job_status_types WHERE type_id = " + type.ordinal() + ";"); //NON-NLS
675  resultSet.next();
676  if (resultSet.getLong("count") == 0) {
677  throw ex;
678  }
679  resultSet.close();
680  resultSet = null;
681  }
682  }
683  } finally {
684  closeResultSet(resultSet);
685  closeStatement(statement);
687  }
688  }
689 
696  private void initReviewStatuses(CaseDbConnection connection) throws SQLException, TskCoreException {
697  Statement statement = null;
698  ResultSet resultSet = null;
700  try {
701  statement = connection.createStatement();
702  for (BlackboardArtifact.ReviewStatus status : BlackboardArtifact.ReviewStatus.values()) {
703  try {
704  String query = "INSERT INTO review_statuses (review_status_id, review_status_name, display_name) " //NON-NLS
705  + "VALUES (" + status.getID() + ",'" + status.getName() + "','" + status.getDisplayName() + "')";
706  if (getDatabaseType().equals(DbType.POSTGRESQL)) {
707  query += " ON CONFLICT ON CONSTRAINT review_statuses_pkey DO NOTHING"; // NON-NLS
708  }
709  statement.execute(query);
710  } catch (SQLException ex) {
711  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) as count FROM review_statuses WHERE review_status_id = " + status.getID()); //NON-NLS
712  resultSet.next();
713  if (resultSet.getLong("count") == 0) {
714  throw ex;
715  }
716  resultSet.close();
717  resultSet = null;
718  }
719  }
720  } finally {
721  closeResultSet(resultSet);
722  closeStatement(statement);
724  }
725  }
726 
734  private void initEncodingTypes(CaseDbConnection connection) throws SQLException, TskCoreException {
735  Statement statement = null;
736  ResultSet resultSet = null;
738  try {
739  statement = connection.createStatement();
740  for (TskData.EncodingType type : TskData.EncodingType.values()) {
741  try {
742  String query = "INSERT INTO file_encoding_types (encoding_type, name) VALUES (" + type.getType() + " , '" + type.name() + "')"; // NON-NLS
743  if (getDatabaseType().equals(DbType.POSTGRESQL)) {
744  query += " ON CONFLICT ON CONSTRAINT file_encoding_types_pkey DO NOTHING"; // NON-NLS
745  }
746  statement.execute(query);
747  } catch (SQLException ex) {
748  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) as count FROM file_encoding_types WHERE encoding_type = " + type.getType()); //NON-NLS
749  resultSet.next();
750  if (resultSet.getLong("count") == 0) {
751  throw ex;
752  }
753  resultSet.close();
754  resultSet = null;
755  }
756  }
757  } finally {
758  closeResultSet(resultSet);
759  closeStatement(statement);
761  }
762  }
763 
772  private void updateExaminers(CaseDbConnection connection) throws SQLException, TskCoreException {
773 
774  String loginName = System.getProperty("user.name");
775  if (loginName.isEmpty()) {
776  logger.log(Level.SEVERE, "Cannot determine logged in user name");
777  return;
778  }
779 
781  try {
782  PreparedStatement statement;
783  switch (getDatabaseType()) {
784  case POSTGRESQL:
785  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_EXAMINER_POSTGRESQL);
786  break;
787  case SQLITE:
788  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_EXAMINER_SQLITE);
789  break;
790  default:
791  throw new TskCoreException("Unknown DB Type: " + getDatabaseType().name());
792  }
793  statement.clearParameters();
794  statement.setString(1, loginName);
795  connection.executeUpdate(statement);
796  } catch (SQLException ex) {
797  throw new TskCoreException("Error inserting row in tsk_examiners. login name: " + loginName, ex);
798  } finally {
800  }
801  }
802 
810  private void populateHasChildrenMap(CaseDbConnection connection) throws TskCoreException {
811  long timestamp = System.currentTimeMillis();
812 
813  Statement statement = null;
814  ResultSet resultSet = null;
816  try {
817  statement = connection.createStatement();
818  resultSet = statement.executeQuery("select distinct par_obj_id from tsk_objects"); //NON-NLS
819 
820  synchronized (hasChildrenBitSetMap) {
821  while (resultSet.next()) {
822  setHasChildren(resultSet.getLong("par_obj_id"));
823  }
824  }
825  long delay = System.currentTimeMillis() - timestamp;
826  logger.log(Level.INFO, "Time to initialize parent node cache: {0} ms", delay); //NON-NLS
827  } catch (SQLException ex) {
828  throw new TskCoreException("Error populating parent node cache", ex);
829  } finally {
830  closeResultSet(resultSet);
831  closeStatement(statement);
833  }
834  }
835 
842  void addDataSourceToHasChildrenMap() throws TskCoreException {
843 
844  CaseDbConnection connection = connections.getConnection();
845  try {
846  populateHasChildrenMap(connection);
847  } finally {
848  closeConnection(connection);
849  }
850  }
851 
861  private void updateDatabaseSchema(String dbPath) throws Exception {
862  CaseDbConnection connection = null;
863  ResultSet resultSet = null;
864  Statement statement = null;
866  try {
867  connection = connections.getConnection();
868  connection.beginTransaction();
869 
870  boolean hasMinorVersion = false;
871  ResultSet columns = connection.getConnection().getMetaData().getColumns(null, null, "tsk_db_info", "schema%");
872  while (columns.next()) {
873  if (columns.getString("COLUMN_NAME").equals("schema_minor_ver")) {
874  hasMinorVersion = true;
875  }
876  }
877 
878  // Get the schema version number of the case database from the tsk_db_info table.
879  int dbSchemaMajorVersion;
880  int dbSchemaMinorVersion = 0; //schemas before 7 have no minor version , default it to zero.
881 
882  statement = connection.createStatement();
883  resultSet = connection.executeQuery(statement, "SELECT schema_ver"
884  + (hasMinorVersion ? ", schema_minor_ver" : "")
885  + " FROM tsk_db_info"); //NON-NLS
886  if (resultSet.next()) {
887  dbSchemaMajorVersion = resultSet.getInt("schema_ver"); //NON-NLS
888  if (hasMinorVersion) {
889  //if there is a minor version column, use it, else default to zero.
890  dbSchemaMinorVersion = resultSet.getInt("schema_minor_ver"); //NON-NLS
891  }
892  } else {
893  throw new TskCoreException();
894  }
895  CaseDbSchemaVersionNumber dbSchemaVersion = new CaseDbSchemaVersionNumber(dbSchemaMajorVersion, dbSchemaMinorVersion);
896 
897  resultSet.close();
898  resultSet = null;
899  statement.close();
900  statement = null;
901  //check schema compatibility
902  if (false == CURRENT_DB_SCHEMA_VERSION.isCompatible(dbSchemaVersion)) {
903  //we cannot open a db with a major schema version higher than the current one.
904  throw new TskUnsupportedSchemaVersionException(
905  "Unsupported DB schema version " + dbSchemaVersion + ", the highest supported schema version is " + CURRENT_DB_SCHEMA_VERSION.getMajor() + ".X");
906  } else if (dbSchemaVersion.compareTo(CURRENT_DB_SCHEMA_VERSION) < 0) {
907  //The schema version is compatible,possibly after upgrades.
908 
909  if (null != dbPath) {
910  // Make a backup copy of the database. Client code can get the path of the backup
911  // using the getBackupDatabasePath() method.
912  String backupFilePath = dbPath + ".schemaVer" + dbSchemaVersion.toString() + ".backup"; //NON-NLS
913  copyCaseDB(backupFilePath);
914  dbBackupPath = backupFilePath;
915  }
916 
917  // ***CALL SCHEMA UPDATE METHODS HERE***
918  // Each method should examine the schema version passed to it and either:
919  // a. do nothing and return the schema version unchanged, or
920  // b. upgrade the database and return the schema version that the db was upgraded to.
921  dbSchemaVersion = updateFromSchema2toSchema3(dbSchemaVersion, connection);
922  dbSchemaVersion = updateFromSchema3toSchema4(dbSchemaVersion, connection);
923  dbSchemaVersion = updateFromSchema4toSchema5(dbSchemaVersion, connection);
924  dbSchemaVersion = updateFromSchema5toSchema6(dbSchemaVersion, connection);
925  dbSchemaVersion = updateFromSchema6toSchema7(dbSchemaVersion, connection);
926  dbSchemaVersion = updateFromSchema7toSchema7dot1(dbSchemaVersion, connection);
927  dbSchemaVersion = updateFromSchema7dot1toSchema7dot2(dbSchemaVersion, connection);
928  dbSchemaVersion = updateFromSchema7dot2toSchema8dot0(dbSchemaVersion, connection);
929  dbSchemaVersion = updateFromSchema8dot0toSchema8dot1(dbSchemaVersion, connection);
930  dbSchemaVersion = updateFromSchema8dot1toSchema8dot2(dbSchemaVersion, connection);
931  dbSchemaVersion = updateFromSchema8dot2toSchema8dot3(dbSchemaVersion, connection);
932  dbSchemaVersion = updateFromSchema8dot3toSchema8dot4(dbSchemaVersion, connection);
933  dbSchemaVersion = updateFromSchema8dot4toSchema8dot5(dbSchemaVersion, connection);
934  dbSchemaVersion = updateFromSchema8dot5toSchema8dot6(dbSchemaVersion, connection);
935  dbSchemaVersion = updateFromSchema8dot6toSchema9dot0(dbSchemaVersion, connection);
936  dbSchemaVersion = updateFromSchema9dot0toSchema9dot1(dbSchemaVersion, connection);
937  dbSchemaVersion = updateFromSchema9dot1toSchema9dot2(dbSchemaVersion, connection);
938  dbSchemaVersion = updateFromSchema9dot2toSchema9dot3(dbSchemaVersion, connection);
939 
940 
941 
942  statement = connection.createStatement();
943  connection.executeUpdate(statement, "UPDATE tsk_db_info SET schema_ver = " + dbSchemaVersion.getMajor() + ", schema_minor_ver = " + dbSchemaVersion.getMinor()); //NON-NLS
944  connection.executeUpdate(statement, "UPDATE tsk_db_info_extended SET value = " + dbSchemaVersion.getMajor() + " WHERE name = '" + SCHEMA_MAJOR_VERSION_KEY + "'"); //NON-NLS
945  connection.executeUpdate(statement, "UPDATE tsk_db_info_extended SET value = " + dbSchemaVersion.getMinor() + " WHERE name = '" + SCHEMA_MINOR_VERSION_KEY + "'"); //NON-NLS
946  statement.close();
947  statement = null;
948  }
949 
950  connection.commitTransaction();
951  } catch (Exception ex) { // Cannot do exception multi-catch in Java 6, so use catch-all.
952  rollbackTransaction(connection);
953  throw ex;
954  } finally {
955  closeResultSet(resultSet);
956  closeStatement(statement);
957  closeConnection(connection);
959  }
960  }
961 
969  private void initDBSchemaCreationVersion(CaseDbConnection connection) throws SQLException {
970 
971  Statement statement = null;
972  ResultSet resultSet = null;
973  String createdSchemaMajorVersion = "0";
974  String createdSchemaMinorVersion = "0";
976  try {
977  statement = connection.createStatement();
978  resultSet = connection.executeQuery(statement, "SELECT name, value FROM tsk_db_info_extended");
979  while (resultSet.next()) {
980  String name = resultSet.getString("name");
981  if (name.equals(CREATION_SCHEMA_MAJOR_VERSION_KEY) || name.equals("CREATED_SCHEMA_MAJOR_VERSION")) {
982  createdSchemaMajorVersion = resultSet.getString("value");
983  } else if (name.equals(CREATION_SCHEMA_MINOR_VERSION_KEY) || name.equals("CREATED_SCHEMA_MINOR_VERSION")) {
984  createdSchemaMinorVersion = resultSet.getString("value");
985  }
986  }
987 
988  } finally {
989  closeResultSet(resultSet);
990  closeStatement(statement);
992  }
993 
994  caseDBSchemaCreationVersion = new CaseDbSchemaVersionNumber(Integer.parseInt(createdSchemaMajorVersion), Integer.parseInt(createdSchemaMinorVersion));
995  }
996 
1006  public void copyCaseDB(String newDBPath) throws IOException {
1007  if (dbPath.isEmpty()) {
1008  throw new IOException("Copying case database files is not supported for this type of case database"); //NON-NLS
1009  }
1010  InputStream in = null;
1011  OutputStream out = null;
1013  try {
1014  InputStream inFile = new FileInputStream(dbPath);
1015  in = new BufferedInputStream(inFile);
1016  OutputStream outFile = new FileOutputStream(newDBPath);
1017  out = new BufferedOutputStream(outFile);
1018  int bytesRead = in.read();
1019  while (bytesRead != -1) {
1020  out.write(bytesRead);
1021  bytesRead = in.read();
1022  }
1023  } finally {
1024  try {
1025  if (in != null) {
1026  in.close();
1027  }
1028  if (out != null) {
1029  out.flush();
1030  out.close();
1031  }
1032  } catch (IOException e) {
1033  logger.log(Level.WARNING, "Could not close streams after db copy", e); //NON-NLS
1034  }
1036  }
1037  }
1038 
1042  private void logSQLiteJDBCDriverInfo() {
1043  try {
1044  SleuthkitCase.logger.info(String.format("sqlite-jdbc version %s loaded in %s mode", //NON-NLS
1045  SQLiteJDBCLoader.getVersion(), SQLiteJDBCLoader.isNativeMode()
1046  ? "native" : "pure-java")); //NON-NLS
1047  } catch (Exception ex) {
1048  SleuthkitCase.logger.log(Level.SEVERE, "Error querying case database mode", ex);
1049  }
1050  }
1051 
1065  @SuppressWarnings("deprecation")
1066  private CaseDbSchemaVersionNumber updateFromSchema2toSchema3(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
1067  if (schemaVersion.getMajor() != 2) {
1068  return schemaVersion;
1069  }
1070  Statement statement = null;
1071  Statement statement2 = null;
1072  Statement updateStatement = null;
1073  ResultSet resultSet = null;
1075  try {
1076  statement = connection.createStatement();
1077  statement2 = connection.createStatement();
1078 
1079  // Add new tables for tags.
1080  statement.execute("CREATE TABLE tag_names (tag_name_id INTEGER PRIMARY KEY, display_name TEXT UNIQUE, description TEXT NOT NULL, color TEXT NOT NULL)"); //NON-NLS
1081  statement.execute("CREATE TABLE content_tags (tag_id INTEGER PRIMARY KEY, obj_id INTEGER NOT NULL, tag_name_id INTEGER NOT NULL, comment TEXT NOT NULL, begin_byte_offset INTEGER NOT NULL, end_byte_offset INTEGER NOT NULL)"); //NON-NLS
1082  statement.execute("CREATE TABLE blackboard_artifact_tags (tag_id INTEGER PRIMARY KEY, artifact_id INTEGER NOT NULL, tag_name_id INTEGER NOT NULL, comment TEXT NOT NULL)"); //NON-NLS
1083 
1084  // Add a new table for reports.
1085  statement.execute("CREATE TABLE reports (report_id INTEGER PRIMARY KEY, path TEXT NOT NULL, crtime INTEGER NOT NULL, src_module_name TEXT NOT NULL, report_name TEXT NOT NULL)"); //NON-NLS
1086 
1087  // Add new columns to the image info table.
1088  statement.execute("ALTER TABLE tsk_image_info ADD COLUMN size INTEGER;"); //NON-NLS
1089  statement.execute("ALTER TABLE tsk_image_info ADD COLUMN md5 TEXT;"); //NON-NLS
1090  statement.execute("ALTER TABLE tsk_image_info ADD COLUMN display_name TEXT;"); //NON-NLS
1091 
1092  // Add a new column to the file system info table.
1093  statement.execute("ALTER TABLE tsk_fs_info ADD COLUMN display_name TEXT;"); //NON-NLS
1094 
1095  // Add a new column to the file table.
1096  statement.execute("ALTER TABLE tsk_files ADD COLUMN meta_seq INTEGER;"); //NON-NLS
1097 
1098  // Add new columns and indexes to the attributes table and populate the
1099  // new column. Note that addition of the new column is a denormalization
1100  // to optimize attribute queries.
1101  statement.execute("ALTER TABLE blackboard_attributes ADD COLUMN artifact_type_id INTEGER NULL NOT NULL DEFAULT -1;"); //NON-NLS
1102  statement.execute("CREATE INDEX attribute_artifactTypeId ON blackboard_attributes(artifact_type_id);"); //NON-NLS
1103  statement.execute("CREATE INDEX attribute_valueText ON blackboard_attributes(value_text);"); //NON-NLS
1104  statement.execute("CREATE INDEX attribute_valueInt32 ON blackboard_attributes(value_int32);"); //NON-NLS
1105  statement.execute("CREATE INDEX attribute_valueInt64 ON blackboard_attributes(value_int64);"); //NON-NLS
1106  statement.execute("CREATE INDEX attribute_valueDouble ON blackboard_attributes(value_double);"); //NON-NLS
1107  resultSet = statement.executeQuery("SELECT attrs.artifact_id AS artifact_id, " //NON-NLS
1108  + "arts.artifact_type_id AS artifact_type_id " //NON-NLS
1109  + "FROM blackboard_attributes AS attrs " //NON-NLS
1110  + "INNER JOIN blackboard_artifacts AS arts " //NON-NLS
1111  + "WHERE attrs.artifact_id = arts.artifact_id;"); //NON-NLS
1112  updateStatement = connection.createStatement();
1113  while (resultSet.next()) {
1114  long artifactId = resultSet.getLong("artifact_id");
1115  int artifactTypeId = resultSet.getInt("artifact_type_id");
1116  updateStatement.executeUpdate(
1117  "UPDATE blackboard_attributes " //NON-NLS
1118  + "SET artifact_type_id = " + artifactTypeId //NON-NLS
1119  + " WHERE blackboard_attributes.artifact_id = " + artifactId + ";"); //NON-NLS
1120  }
1121  resultSet.close();
1122 
1123  // Convert existing tag artifact and attribute rows to rows in the new tags tables.
1124  Map<String, Long> tagNames = new HashMap<>();
1125  long tagNameCounter = 1;
1126 
1127  // Convert file tags.
1128  // We need data from the TSK_TAG_NAME and TSK_COMMENT attributes, and need the file size from the tsk_files table.
1129  resultSet = statement.executeQuery("SELECT * FROM \n"
1130  + "(SELECT blackboard_artifacts.obj_id AS objId, blackboard_attributes.artifact_id AS artifactId, blackboard_attributes.value_text AS name\n"
1131  + "FROM blackboard_artifacts INNER JOIN blackboard_attributes \n"
1132  + "ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id \n"
1133  + "WHERE blackboard_artifacts.artifact_type_id = "
1134  + BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID()
1135  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TAG_NAME.getTypeID()
1136  + ") AS tagNames \n"
1137  + "INNER JOIN \n"
1138  + "(SELECT tsk_files.obj_id as objId2, tsk_files.size AS fileSize \n"
1139  + "FROM blackboard_artifacts INNER JOIN tsk_files \n"
1140  + "ON blackboard_artifacts.obj_id = tsk_files.obj_id) AS fileData \n"
1141  + "ON tagNames.objId = fileData.objId2 \n"
1142  + "LEFT JOIN \n"
1143  + "(SELECT value_text AS comment, artifact_id AS tagArtifactId FROM blackboard_attributes WHERE attribute_type_id = "
1144  + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT.getTypeID() + ") AS tagComments \n"
1145  + "ON tagNames.artifactId = tagComments.tagArtifactId");
1146 
1147  while (resultSet.next()) {
1148  long objId = resultSet.getLong("objId");
1149  long fileSize = resultSet.getLong("fileSize");
1150  String tagName = resultSet.getString("name");
1151  String tagComment = resultSet.getString("comment");
1152  if (tagComment == null) {
1153  tagComment = "";
1154  }
1155 
1156  if (tagName != null && !tagName.isEmpty()) {
1157  // Get the index for the tag name, adding it to the database if needed.
1158  long tagNameIndex;
1159  if (tagNames.containsKey(tagName)) {
1160  tagNameIndex = tagNames.get(tagName);
1161  } else {
1162  statement2.execute("INSERT INTO tag_names (display_name, description, color) "
1163  + "VALUES(\"" + tagName + "\", \"\", \"None\")");
1164  tagNames.put(tagName, tagNameCounter);
1165  tagNameIndex = tagNameCounter;
1166  tagNameCounter++;
1167  }
1168 
1169  statement2.execute("INSERT INTO content_tags (obj_id, tag_name_id, comment, begin_byte_offset, end_byte_offset) "
1170  + "VALUES(" + objId + ", " + tagNameIndex + ", \"" + tagComment + "\", 0, " + fileSize + ")");
1171  }
1172  }
1173  resultSet.close();
1174 
1175  // Convert artifact tags.
1176  // We need data from the TSK_TAG_NAME, TSK_TAGGED_ARTIFACT, and TSK_COMMENT attributes.
1177  resultSet = statement.executeQuery("SELECT * FROM \n"
1178  + "(SELECT blackboard_artifacts.obj_id AS objId, blackboard_attributes.artifact_id AS artifactId, "
1179  + "blackboard_attributes.value_text AS name\n"
1180  + "FROM blackboard_artifacts INNER JOIN blackboard_attributes \n"
1181  + "ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id \n"
1182  + "WHERE blackboard_artifacts.artifact_type_id = "
1183  + BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()
1184  + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TAG_NAME.getTypeID()
1185  + ") AS tagNames \n"
1186  + "INNER JOIN \n"
1187  + "(SELECT value_int64 AS taggedArtifactId, artifact_id AS associatedArtifactId FROM blackboard_attributes WHERE attribute_type_id = "
1188  + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID() + ") AS tagArtifacts \n"
1189  + "ON tagNames.artifactId = tagArtifacts.associatedArtifactId \n"
1190  + "LEFT JOIN \n"
1191  + "(SELECT value_text AS comment, artifact_id AS commentArtifactId FROM blackboard_attributes WHERE attribute_type_id = "
1192  + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT.getTypeID() + ") AS tagComments \n"
1193  + "ON tagNames.artifactId = tagComments.commentArtifactId");
1194 
1195  while (resultSet.next()) {
1196  long artifactId = resultSet.getLong("taggedArtifactId");
1197  String tagName = resultSet.getString("name");
1198  String tagComment = resultSet.getString("comment");
1199  if (tagComment == null) {
1200  tagComment = "";
1201  }
1202  if (tagName != null && !tagName.isEmpty()) {
1203  // Get the index for the tag name, adding it to the database if needed.
1204  long tagNameIndex;
1205  if (tagNames.containsKey(tagName)) {
1206  tagNameIndex = tagNames.get(tagName);
1207  } else {
1208  statement2.execute("INSERT INTO tag_names (display_name, description, color) "
1209  + "VALUES(\"" + tagName + "\", \"\", \"None\")");
1210  tagNames.put(tagName, tagNameCounter);
1211  tagNameIndex = tagNameCounter;
1212  tagNameCounter++;
1213  }
1214 
1215  statement2.execute("INSERT INTO blackboard_artifact_tags (artifact_id, tag_name_id, comment) "
1216  + "VALUES(" + artifactId + ", " + tagNameIndex + ", \"" + tagComment + "\")");
1217  }
1218  }
1219  resultSet.close();
1220 
1221  statement.execute(
1222  "DELETE FROM blackboard_attributes WHERE artifact_id IN " //NON-NLS
1223  + "(SELECT artifact_id FROM blackboard_artifacts WHERE artifact_type_id = " //NON-NLS
1224  + ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID()
1225  + " OR artifact_type_id = " + ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID() + ");"); //NON-NLS
1226  statement.execute(
1227  "DELETE FROM blackboard_artifacts WHERE artifact_type_id = " //NON-NLS
1228  + ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID()
1229  + " OR artifact_type_id = " + ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID() + ";"); //NON-NLS
1230 
1231  return new CaseDbSchemaVersionNumber(3, 0);
1232  } finally {
1233  closeStatement(updateStatement);
1234  closeResultSet(resultSet);
1235  closeStatement(statement);
1236  closeStatement(statement2);
1238  }
1239  }
1240 
1254  private CaseDbSchemaVersionNumber updateFromSchema3toSchema4(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
1255  if (schemaVersion.getMajor() != 3) {
1256  return schemaVersion;
1257  }
1258 
1259  Statement statement = null;
1260  ResultSet resultSet = null;
1261  Statement queryStatement = null;
1262  ResultSet queryResultSet = null;
1263  Statement updateStatement = null;
1265  try {
1266  // Add mime_type column to tsk_files table. Populate with general
1267  // info artifact file signature data.
1268  statement = connection.createStatement();
1269  updateStatement = connection.createStatement();
1270  statement.execute("ALTER TABLE tsk_files ADD COLUMN mime_type TEXT;");
1271  resultSet = statement.executeQuery("SELECT files.obj_id AS obj_id, attrs.value_text AS value_text "
1272  + "FROM tsk_files AS files, blackboard_attributes AS attrs, blackboard_artifacts AS arts "
1273  + "WHERE files.obj_id = arts.obj_id AND "
1274  + "arts.artifact_id = attrs.artifact_id AND "
1275  + "arts.artifact_type_id = 1 AND "
1276  + "attrs.attribute_type_id = 62");
1277  while (resultSet.next()) {
1278  updateStatement.executeUpdate(
1279  "UPDATE tsk_files " //NON-NLS
1280  + "SET mime_type = '" + resultSet.getString("value_text") + "' " //NON-NLS
1281  + "WHERE tsk_files.obj_id = " + resultSet.getInt("obj_id") + ";"); //NON-NLS
1282  }
1283  resultSet.close();
1284 
1285  // Add value_type column to blackboard_attribute_types table.
1286  statement.execute("ALTER TABLE blackboard_attribute_types ADD COLUMN value_type INTEGER NOT NULL DEFAULT -1;");
1287  resultSet = statement.executeQuery("SELECT * FROM blackboard_attribute_types AS types"); //NON-NLS
1288  while (resultSet.next()) {
1289  int attributeTypeId = resultSet.getInt("attribute_type_id");
1290  String attributeLabel = resultSet.getString("type_name");
1291  if (attributeTypeId < Blackboard.MIN_USER_DEFINED_TYPE_ID) {
1292  updateStatement.executeUpdate(
1293  "UPDATE blackboard_attribute_types " //NON-NLS
1294  + "SET value_type = " + ATTRIBUTE_TYPE.fromLabel(attributeLabel).getValueType().getType() + " " //NON-NLS
1295  + "WHERE blackboard_attribute_types.attribute_type_id = " + attributeTypeId + ";"); //NON-NLS
1296  }
1297  }
1298  resultSet.close();
1299 
1300  // Add a data_sources_info table.
1301  queryStatement = connection.createStatement();
1302  statement.execute("CREATE TABLE data_source_info (obj_id INTEGER PRIMARY KEY, device_id TEXT NOT NULL, time_zone TEXT NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id));");
1303  resultSet = statement.executeQuery("SELECT * FROM tsk_objects WHERE par_obj_id IS NULL");
1304  while (resultSet.next()) {
1305  long objectId = resultSet.getLong("obj_id");
1306  String timeZone = "";
1307  queryResultSet = queryStatement.executeQuery("SELECT tzone FROM tsk_image_info WHERE obj_id = " + objectId);
1308  if (queryResultSet.next()) {
1309  timeZone = queryResultSet.getString("tzone");
1310  }
1311  queryResultSet.close();
1312  updateStatement.executeUpdate("INSERT INTO data_source_info (obj_id, device_id, time_zone) "
1313  + "VALUES(" + objectId + ", '" + UUID.randomUUID().toString() + "' , '" + timeZone + "');");
1314  }
1315  resultSet.close();
1316 
1317  // Add data_source_obj_id column to the tsk_files table.
1318  //
1319  // NOTE: A new case database will have the following FK constraint:
1320  //
1321  // REFERENCES data_source_info (obj_id)
1322  //
1323  // The constraint is sacrificed here to avoid having to create and
1324  // populate a new tsk_files table.
1325  //
1326  // TODO: Do this right.
1327  statement.execute("ALTER TABLE tsk_files ADD COLUMN data_source_obj_id BIGINT NOT NULL DEFAULT -1;");
1328  resultSet = statement.executeQuery("SELECT tsk_files.obj_id AS obj_id, par_obj_id FROM tsk_files, tsk_objects WHERE tsk_files.obj_id = tsk_objects.obj_id");
1329  while (resultSet.next()) {
1330  long fileId = resultSet.getLong("obj_id");
1331  long dataSourceId = getDataSourceObjectId(connection, fileId);
1332  updateStatement.executeUpdate("UPDATE tsk_files SET data_source_obj_id = " + dataSourceId + " WHERE obj_id = " + fileId + ";");
1333  }
1334  resultSet.close();
1335  statement.execute("CREATE TABLE ingest_module_types (type_id INTEGER PRIMARY KEY, type_name TEXT NOT NULL)"); //NON-NLS
1336  statement.execute("CREATE TABLE ingest_job_status_types (type_id INTEGER PRIMARY KEY, type_name TEXT NOT NULL)"); //NON-NLS
1337  if (this.dbType.equals(DbType.SQLITE)) {
1338  statement.execute("CREATE TABLE ingest_modules (ingest_module_id INTEGER PRIMARY KEY, display_name TEXT NOT NULL, unique_name TEXT UNIQUE NOT NULL, type_id INTEGER NOT NULL, version TEXT NOT NULL, FOREIGN KEY(type_id) REFERENCES ingest_module_types(type_id));"); //NON-NLS
1339  statement.execute("CREATE TABLE ingest_jobs (ingest_job_id INTEGER PRIMARY KEY, obj_id BIGINT NOT NULL, host_name TEXT NOT NULL, start_date_time BIGINT NOT NULL, end_date_time BIGINT NOT NULL, status_id INTEGER NOT NULL, settings_dir TEXT, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id), FOREIGN KEY(status_id) REFERENCES ingest_job_status_types(type_id));"); //NON-NLS
1340  } else {
1341  statement.execute("CREATE TABLE ingest_modules (ingest_module_id BIGSERIAL PRIMARY KEY, display_name TEXT NOT NULL, unique_name TEXT UNIQUE NOT NULL, type_id INTEGER NOT NULL, version TEXT NOT NULL, FOREIGN KEY(type_id) REFERENCES ingest_module_types(type_id));"); //NON-NLS
1342  statement.execute("CREATE TABLE ingest_jobs (ingest_job_id BIGSERIAL PRIMARY KEY, obj_id BIGINT NOT NULL, host_name TEXT NOT NULL, start_date_time BIGINT NOT NULL, end_date_time BIGINT NOT NULL, status_id INTEGER NOT NULL, settings_dir TEXT, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id), FOREIGN KEY(status_id) REFERENCES ingest_job_status_types(type_id));"); //NON-NLS
1343  }
1344 
1345  statement.execute("CREATE TABLE ingest_job_modules (ingest_job_id INTEGER, ingest_module_id INTEGER, pipeline_position INTEGER, PRIMARY KEY(ingest_job_id, ingest_module_id), FOREIGN KEY(ingest_job_id) REFERENCES ingest_jobs(ingest_job_id), FOREIGN KEY(ingest_module_id) REFERENCES ingest_modules(ingest_module_id));"); //NON-NLS
1346  initIngestModuleTypes(connection);
1347  initIngestStatusTypes(connection);
1348 
1349  return new CaseDbSchemaVersionNumber(4, 0);
1350 
1351  } finally {
1352  closeResultSet(queryResultSet);
1353  closeStatement(queryStatement);
1354  closeStatement(updateStatement);
1355  closeResultSet(resultSet);
1356  closeStatement(statement);
1358  }
1359  }
1360 
1374  private CaseDbSchemaVersionNumber updateFromSchema4toSchema5(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
1375  if (schemaVersion.getMajor() != 4) {
1376  return schemaVersion;
1377  }
1378 
1379  Statement statement = null;
1381  try {
1382  // Add the review_statuses lookup table.
1383  statement = connection.createStatement();
1384  statement.execute("CREATE TABLE review_statuses (review_status_id INTEGER PRIMARY KEY, review_status_name TEXT NOT NULL, display_name TEXT NOT NULL)");
1385 
1386  /*
1387  * Add review_status_id column to artifacts table.
1388  *
1389  * NOTE: For DBs created with schema 5 we define a foreign key
1390  * constraint on the review_status_column. We don't bother with this
1391  * for DBs updated to schema 5 because of limitations of the SQLite
1392  * ALTER TABLE command.
1393  */
1394  statement.execute("ALTER TABLE blackboard_artifacts ADD COLUMN review_status_id INTEGER NOT NULL DEFAULT " + BlackboardArtifact.ReviewStatus.UNDECIDED.getID());
1395 
1396  // Add the encoding table
1397  statement.execute("CREATE TABLE file_encoding_types (encoding_type INTEGER PRIMARY KEY, name TEXT NOT NULL);");
1398  initEncodingTypes(connection);
1399 
1400  /*
1401  * This needs to be done due to a Autopsy/TSK out of synch problem.
1402  * Without this, it is possible to upgrade from version 4 to 5 and
1403  * then 5 to 6, but not from 4 to 6.
1404  */
1405  initReviewStatuses(connection);
1406 
1407  // Add encoding type column to tsk_files_path
1408  // This should really have the FOREIGN KEY constraint but there are problems
1409  // getting that to work, so we don't add it on this upgrade path.
1410  statement.execute("ALTER TABLE tsk_files_path ADD COLUMN encoding_type INTEGER NOT NULL DEFAULT 0;");
1411 
1412  return new CaseDbSchemaVersionNumber(5, 0);
1413 
1414  } finally {
1415  closeStatement(statement);
1417  }
1418  }
1419 
1433  private CaseDbSchemaVersionNumber updateFromSchema5toSchema6(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
1434  if (schemaVersion.getMajor() != 5) {
1435  return schemaVersion;
1436  }
1437 
1438  /*
1439  * This upgrade fixes a bug where some releases had artifact review
1440  * status support in the case database and others did not.
1441  */
1442  Statement statement = null;
1443  ResultSet resultSet = null;
1445  try {
1446  /*
1447  * Add the review_statuses lookup table, if missing.
1448  */
1449  statement = connection.createStatement();
1450  statement.execute("CREATE TABLE IF NOT EXISTS review_statuses (review_status_id INTEGER PRIMARY KEY, review_status_name TEXT NOT NULL, display_name TEXT NOT NULL)");
1451 
1452  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM review_statuses"); //NON-NLS
1453  resultSet.next();
1454  if (resultSet.getLong("count") == 0) {
1455  /*
1456  * Add review_status_id column to artifacts table.
1457  *
1458  * NOTE: For DBs created with schema 5 or 6 we define a foreign
1459  * key constraint on the review_status_column. We don't bother
1460  * with this for DBs updated to schema 5 or 6 because of
1461  * limitations of the SQLite ALTER TABLE command.
1462  */
1463  statement.execute("ALTER TABLE blackboard_artifacts ADD COLUMN review_status_id INTEGER NOT NULL DEFAULT " + BlackboardArtifact.ReviewStatus.UNDECIDED.getID());
1464  }
1465 
1466  return new CaseDbSchemaVersionNumber(6, 0);
1467 
1468  } finally {
1469  closeResultSet(resultSet);
1470  closeStatement(statement);
1472  }
1473  }
1474 
1488  private CaseDbSchemaVersionNumber updateFromSchema6toSchema7(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
1489  if (schemaVersion.getMajor() != 6) {
1490  return schemaVersion;
1491  }
1492 
1493  /*
1494  * This upgrade adds an indexed extension column to the tsk_files table.
1495  */
1496  Statement statement = null;
1497  Statement updstatement = null;
1498  ResultSet resultSet = null;
1500  try {
1501  statement = connection.createStatement();
1502  updstatement = connection.createStatement();
1503  statement.execute("ALTER TABLE tsk_files ADD COLUMN extension TEXT");
1504 
1505  resultSet = connection.executeQuery(statement, "SELECT obj_id,name FROM tsk_files"); //NON-NLS
1506  while (resultSet.next()) {
1507  long objID = resultSet.getLong("obj_id");
1508  String name = resultSet.getString("name");
1509  updstatement.executeUpdate("UPDATE tsk_files SET extension = '" + escapeSingleQuotes(extractExtension(name)) + "' "
1510  + "WHERE obj_id = " + objID);
1511  }
1512 
1513  statement.execute("CREATE INDEX file_extension ON tsk_files ( extension )");
1514 
1515  // Add artifact_obj_id column to blackboard_artifacts table, data conversion for old versions isn't necesarry.
1516  statement.execute("ALTER TABLE blackboard_artifacts ADD COLUMN artifact_obj_id INTEGER NOT NULL DEFAULT -1");
1517 
1518  return new CaseDbSchemaVersionNumber(7, 0);
1519 
1520  } finally {
1521  closeResultSet(resultSet);
1522  closeStatement(statement);
1523  closeStatement(updstatement);
1525  }
1526  }
1527 
1541  private CaseDbSchemaVersionNumber updateFromSchema7toSchema7dot1(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
1542  if (schemaVersion.getMajor() != 7) {
1543  return schemaVersion;
1544  }
1545 
1546  if (schemaVersion.getMinor() != 0) {
1547  return schemaVersion;
1548  }
1549 
1550  /*
1551  * This upgrade adds a minor version number column.
1552  */
1553  Statement statement = null;
1554  ResultSet resultSet = null;
1556  try {
1557  statement = connection.createStatement();
1558 
1559  //add the schema minor version number column.
1560  if (schemaVersion.getMinor() == 0) {
1561  //add the schema minor version number column.
1562  statement.execute("ALTER TABLE tsk_db_info ADD COLUMN schema_minor_ver INTEGER DEFAULT 1");
1563  }
1564  return new CaseDbSchemaVersionNumber(7, 1);
1565 
1566  } finally {
1567  closeResultSet(resultSet);
1568  closeStatement(statement);
1570  }
1571  }
1572 
1586  private CaseDbSchemaVersionNumber updateFromSchema7dot1toSchema7dot2(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
1587  if (schemaVersion.getMajor() != 7) {
1588  return schemaVersion;
1589  }
1590 
1591  if (schemaVersion.getMinor() != 1) {
1592  return schemaVersion;
1593  }
1594 
1595  Statement statement = null;
1596  Statement updstatement = null;
1597  ResultSet resultSet = null;
1599  try {
1600  //add the data_source_obj_id column to blackboard_artifacts.
1601  statement = connection.createStatement();
1602  statement.execute("ALTER TABLE blackboard_artifacts ADD COLUMN data_source_obj_id INTEGER NOT NULL DEFAULT -1");
1603 
1604  // populate data_source_obj_id for each artifact
1605  updstatement = connection.createStatement();
1606  resultSet = connection.executeQuery(statement, "SELECT artifact_id, obj_id FROM blackboard_artifacts"); //NON-NLS
1607  while (resultSet.next()) {
1608  long artifact_id = resultSet.getLong("artifact_id");
1609  long obj_id = resultSet.getLong("obj_id");
1610  long data_source_obj_id = getDataSourceObjectId(connection, obj_id);
1611  updstatement.executeUpdate("UPDATE blackboard_artifacts SET data_source_obj_id = " + data_source_obj_id + " "
1612  + "WHERE artifact_id = " + artifact_id);
1613  }
1614  closeResultSet(resultSet);
1615  closeStatement(statement);
1616  closeStatement(updstatement);
1617 
1618  /*
1619  * Add a knownStatus column to the tag_names table.
1620  */
1621  statement = connection.createStatement();
1622  statement.execute("ALTER TABLE tag_names ADD COLUMN knownStatus INTEGER NOT NULL DEFAULT " + TskData.FileKnown.UNKNOWN.getFileKnownValue());
1623 
1624  // Create account_types, accounts, and account_relationships table
1625  if (this.dbType.equals(DbType.SQLITE)) {
1626  statement.execute("CREATE TABLE account_types (account_type_id INTEGER PRIMARY KEY, type_name TEXT UNIQUE NOT NULL, display_name TEXT NOT NULL)");
1627  statement.execute("CREATE TABLE accounts (account_id INTEGER PRIMARY KEY, account_type_id INTEGER NOT NULL, account_unique_identifier TEXT NOT NULL, UNIQUE(account_type_id, account_unique_identifier) , FOREIGN KEY(account_type_id) REFERENCES account_types(account_type_id))");
1628  statement.execute("CREATE TABLE account_relationships (relationship_id INTEGER PRIMARY KEY, account1_id INTEGER NOT NULL, account2_id INTEGER NOT NULL, relationship_source_obj_id INTEGER NOT NULL, date_time INTEGER, relationship_type INTEGER NOT NULL, data_source_obj_id INTEGER NOT NULL, UNIQUE(account1_id, account2_id, relationship_source_obj_id), FOREIGN KEY(account1_id) REFERENCES accounts(account_id), FOREIGN KEY(account2_id) REFERENCES accounts(account_id), FOREIGN KEY(relationship_source_obj_id) REFERENCES tsk_objects(obj_id), FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id))");
1629  } else {
1630  statement.execute("CREATE TABLE account_types (account_type_id BIGSERIAL PRIMARY KEY, type_name TEXT UNIQUE NOT NULL, display_name TEXT NOT NULL)");
1631  statement.execute("CREATE TABLE accounts (account_id BIGSERIAL PRIMARY KEY, account_type_id INTEGER NOT NULL, account_unique_identifier TEXT NOT NULL, UNIQUE(account_type_id, account_unique_identifier) , FOREIGN KEY(account_type_id) REFERENCES account_types(account_type_id))");
1632  statement.execute("CREATE TABLE account_relationships (relationship_id BIGSERIAL PRIMARY KEY, account1_id INTEGER NOT NULL, account2_id INTEGER NOT NULL, relationship_source_obj_id INTEGER NOT NULL, date_time BIGINT, relationship_type INTEGER NOT NULL, data_source_obj_id INTEGER NOT NULL, UNIQUE(account1_id, account2_id, relationship_source_obj_id), FOREIGN KEY(account1_id) REFERENCES accounts(account_id), FOREIGN KEY(account2_id) REFERENCES accounts(account_id), FOREIGN KEY(relationship_source_obj_id) REFERENCES tsk_objects(obj_id), FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id))");
1633  }
1634 
1635  // Create indexes
1636  statement.execute("CREATE INDEX artifact_artifact_objID ON blackboard_artifacts(artifact_obj_id)");
1637  statement.execute("CREATE INDEX relationships_account1 ON account_relationships(account1_id)");
1638  statement.execute("CREATE INDEX relationships_account2 ON account_relationships(account2_id)");
1639  statement.execute("CREATE INDEX relationships_relationship_source_obj_id ON account_relationships(relationship_source_obj_id)");
1640  statement.execute("CREATE INDEX relationships_date_time ON account_relationships(date_time)");
1641  statement.execute("CREATE INDEX relationships_relationship_type ON account_relationships(relationship_type)");
1642  statement.execute("CREATE INDEX relationships_data_source_obj_id ON account_relationships(data_source_obj_id)");
1643 
1644  return new CaseDbSchemaVersionNumber(7, 2);
1645  } finally {
1646  closeResultSet(resultSet);
1647  closeStatement(statement);
1648  closeStatement(updstatement);
1650  }
1651  }
1652 
1666  private CaseDbSchemaVersionNumber updateFromSchema7dot2toSchema8dot0(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
1667  if (schemaVersion.getMajor() != 7) {
1668  return schemaVersion;
1669  }
1670 
1671  if (schemaVersion.getMinor() != 2) {
1672  return schemaVersion;
1673  }
1674 
1675  Statement updateSchemaStatement = connection.createStatement();
1676  Statement getExistingReportsStatement = connection.createStatement();
1677  ResultSet resultSet = null;
1678  ResultSet existingReports = null;
1679 
1681  try {
1682  // Update the schema to turn report_id into an object id.
1683 
1684  // Unfortunately, SQLite doesn't support adding a constraint
1685  // to an existing table so we have to rename the old...
1686  updateSchemaStatement.execute("ALTER TABLE reports RENAME TO old_reports");
1687 
1688  // ...create the new...
1689  updateSchemaStatement.execute("CREATE TABLE reports (obj_id BIGSERIAL PRIMARY KEY, path TEXT NOT NULL, crtime INTEGER NOT NULL, src_module_name TEXT NOT NULL, report_name TEXT NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id))");
1690 
1691  // ...add the existing report records back...
1692  existingReports = getExistingReportsStatement.executeQuery("SELECT * FROM old_reports");
1693  while (existingReports.next()) {
1694  String path = existingReports.getString(2);
1695  long crtime = existingReports.getInt(3);
1696  String sourceModule = existingReports.getString(4);
1697  String reportName = existingReports.getString(5);
1698 
1699  PreparedStatement insertObjectStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_OBJECT, Statement.RETURN_GENERATED_KEYS);
1700  insertObjectStatement.clearParameters();
1701  insertObjectStatement.setNull(1, java.sql.Types.BIGINT);
1702  insertObjectStatement.setLong(2, TskData.ObjectType.REPORT.getObjectType());
1703  connection.executeUpdate(insertObjectStatement);
1704  resultSet = insertObjectStatement.getGeneratedKeys();
1705  if (!resultSet.next()) {
1706  throw new TskCoreException(String.format("Failed to INSERT report %s (%s) in tsk_objects table", reportName, path));
1707  }
1708  long objectId = resultSet.getLong(1); //last_insert_rowid()
1709 
1710  // INSERT INTO reports (obj_id, path, crtime, src_module_name, display_name) VALUES (?, ?, ?, ?, ?)
1711  PreparedStatement insertReportStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_REPORT);
1712  insertReportStatement.clearParameters();
1713  insertReportStatement.setLong(1, objectId);
1714  insertReportStatement.setString(2, path);
1715  insertReportStatement.setLong(3, crtime);
1716  insertReportStatement.setString(4, sourceModule);
1717  insertReportStatement.setString(5, reportName);
1718  connection.executeUpdate(insertReportStatement);
1719  }
1720 
1721  // ...and drop the old table.
1722  updateSchemaStatement.execute("DROP TABLE old_reports");
1723 
1724  return new CaseDbSchemaVersionNumber(8, 0);
1725  } finally {
1726  closeResultSet(resultSet);
1727  closeResultSet(existingReports);
1728  closeStatement(updateSchemaStatement);
1729  closeStatement(getExistingReportsStatement);
1731  }
1732  }
1733 
1747  private CaseDbSchemaVersionNumber updateFromSchema8dot0toSchema8dot1(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
1748  if (schemaVersion.getMajor() != 8) {
1749  return schemaVersion;
1750  }
1751 
1752  if (schemaVersion.getMinor() != 0) {
1753  return schemaVersion;
1754  }
1755 
1757 
1758  try (Statement statement = connection.createStatement();) {
1759  // create examiners table
1760  if (this.dbType.equals(DbType.SQLITE)) {
1761  statement.execute("CREATE TABLE tsk_examiners (examiner_id INTEGER PRIMARY KEY, login_name TEXT NOT NULL, display_name TEXT, UNIQUE(login_name) )");
1762  statement.execute("ALTER TABLE content_tags ADD COLUMN examiner_id INTEGER REFERENCES tsk_examiners(examiner_id) DEFAULT NULL");
1763  statement.execute("ALTER TABLE blackboard_artifact_tags ADD COLUMN examiner_id INTEGER REFERENCES tsk_examiners(examiner_id) DEFAULT NULL");
1764  } else {
1765  statement.execute("CREATE TABLE tsk_examiners (examiner_id BIGSERIAL PRIMARY KEY, login_name TEXT NOT NULL, display_name TEXT, UNIQUE(login_name))");
1766  statement.execute("ALTER TABLE content_tags ADD COLUMN examiner_id BIGINT REFERENCES tsk_examiners(examiner_id) DEFAULT NULL");
1767  statement.execute("ALTER TABLE blackboard_artifact_tags ADD COLUMN examiner_id BIGINT REFERENCES tsk_examiners(examiner_id) DEFAULT NULL");
1768  }
1769 
1770  return new CaseDbSchemaVersionNumber(8, 1);
1771  } finally {
1773  }
1774  }
1775 
1789  private CaseDbSchemaVersionNumber updateFromSchema8dot1toSchema8dot2(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
1790  if (schemaVersion.getMajor() != 8) {
1791  return schemaVersion;
1792  }
1793 
1794  if (schemaVersion.getMinor() != 1) {
1795  return schemaVersion;
1796  }
1797 
1799 
1800  try (Statement statement = connection.createStatement();) {
1801  statement.execute("ALTER TABLE tsk_image_info ADD COLUMN sha1 TEXT DEFAULT NULL");
1802  statement.execute("ALTER TABLE tsk_image_info ADD COLUMN sha256 TEXT DEFAULT NULL");
1803 
1804  statement.execute("ALTER TABLE data_source_info ADD COLUMN acquisition_details TEXT");
1805 
1806  /*
1807  * Add new tsk_db_extended_info table with TSK version, creation
1808  * time schema and schema version numbers as the initial data. The
1809  * creation time schema version is set to 0, 0 to indicate that it
1810  * is not known.
1811  */
1812  statement.execute("CREATE TABLE tsk_db_info_extended (name TEXT PRIMARY KEY, value TEXT NOT NULL)");
1813  ResultSet result = statement.executeQuery("SELECT tsk_ver FROM tsk_db_info");
1814  result.next();
1815  statement.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('" + TSK_VERSION_KEY + "', '" + result.getLong("tsk_ver") + "')");
1816  statement.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('" + SCHEMA_MAJOR_VERSION_KEY + "', '8')");
1817  statement.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('" + SCHEMA_MINOR_VERSION_KEY + "', '2')");
1818  statement.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('" + CREATION_SCHEMA_MAJOR_VERSION_KEY + "', '0')");
1819  statement.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('" + CREATION_SCHEMA_MINOR_VERSION_KEY + "', '0')");
1820 
1821  String primaryKeyType;
1822  switch (getDatabaseType()) {
1823  case POSTGRESQL:
1824  primaryKeyType = "BIGSERIAL";
1825  break;
1826  case SQLITE:
1827  primaryKeyType = "INTEGER";
1828  break;
1829  default:
1830  throw new TskCoreException("Unsupported data base type: " + getDatabaseType().toString());
1831  }
1832 
1833  //create and initialize tsk_event_types tables
1834  statement.execute("CREATE TABLE tsk_event_types ("
1835  + " event_type_id " + primaryKeyType + " PRIMARY KEY, "
1836  + " display_name TEXT UNIQUE NOT NULL, "
1837  + " super_type_id INTEGER REFERENCES tsk_event_types(event_type_id) )");
1838  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1839  + " values( 0, 'Event Types', null)");
1840  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1841  + " values(1, 'File System', 0)");
1842  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1843  + " values(2, 'Web Activity', 0)");
1844  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1845  + " values(3, 'Misc Types', 0)");
1846  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1847  + " values(4, 'Modified', 1)");
1848  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1849  + " values(5, 'Accessed', 1)");
1850  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1851  + " values(6, 'Created', 1)");
1852  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1853  + " values(7, 'Changed', 1)");
1854 
1855  //create tsk_events tables
1856  statement.execute("CREATE TABLE tsk_event_descriptions ("
1857  + " event_description_id " + primaryKeyType + " PRIMARY KEY, "
1858  + " full_description TEXT NOT NULL, "
1859  + " med_description TEXT, "
1860  + " short_description TEXT,"
1861  + " data_source_obj_id BIGINT NOT NULL, "
1862  + " file_obj_id BIGINT NOT NULL, "
1863  + " artifact_id BIGINT, "
1864  + " hash_hit INTEGER NOT NULL, " //boolean
1865  + " tagged INTEGER NOT NULL, " //boolean
1866  + " FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id), "
1867  + " FOREIGN KEY(file_obj_id) REFERENCES tsk_files(obj_id), "
1868  + " FOREIGN KEY(artifact_id) REFERENCES blackboard_artifacts(artifact_id))"
1869  );
1870 
1871  statement.execute("CREATE TABLE tsk_events ( "
1872  + " event_id " + primaryKeyType + " PRIMARY KEY, "
1873  + " event_type_id BIGINT NOT NULL REFERENCES tsk_event_types(event_type_id) ,"
1874  + " event_description_id BIGINT NOT NULL REFERENCES tsk_event_descriptions(event_description_id) ,"
1875  + " time INTEGER NOT NULL) "
1876  );
1877 
1878  //create tsk_events indices
1879  statement.execute("CREATE INDEX events_time ON tsk_events(time)");
1880  statement.execute("CREATE INDEX events_type ON tsk_events(event_type_id)");
1881  statement.execute("CREATE INDEX events_data_source_obj_id ON tsk_event_descriptions(data_source_obj_id) ");
1882  statement.execute("CREATE INDEX events_file_obj_id ON tsk_event_descriptions(file_obj_id) ");
1883  statement.execute("CREATE INDEX events_artifact_id ON tsk_event_descriptions(artifact_id) ");
1884  statement.execute("CREATE INDEX events_sub_type_time ON tsk_events(event_type_id, time) ");
1885  return new CaseDbSchemaVersionNumber(8, 2);
1886 
1887  } finally {
1889  }
1890  }
1891 
1905  private CaseDbSchemaVersionNumber updateFromSchema8dot2toSchema8dot3(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
1906  if (schemaVersion.getMajor() != 8) {
1907  return schemaVersion;
1908  }
1909 
1910  if (schemaVersion.getMinor() != 2) {
1911  return schemaVersion;
1912  }
1913 
1915 
1916  ResultSet resultSet = null;
1917 
1918  try (Statement statement = connection.createStatement();) {
1919 
1920  // Add the uniqueness constraint to the tsk_event and tsk_event_description tables.
1921  // Unfortunately, SQLite doesn't support adding a constraint
1922  // to an existing table so we have to rename the old...
1923  String primaryKeyType;
1924  switch (getDatabaseType()) {
1925  case POSTGRESQL:
1926  primaryKeyType = "BIGSERIAL";
1927  break;
1928  case SQLITE:
1929  primaryKeyType = "INTEGER";
1930  break;
1931  default:
1932  throw new TskCoreException("Unsupported data base type: " + getDatabaseType().toString());
1933  }
1934 
1935  //create and initialize tsk_event_types tables which may or may not exist
1936  statement.execute("CREATE TABLE IF NOT EXISTS tsk_event_types ("
1937  + " event_type_id " + primaryKeyType + " PRIMARY KEY, "
1938  + " display_name TEXT UNIQUE NOT NULL, "
1939  + " super_type_id INTEGER REFERENCES tsk_event_types(event_type_id) )");
1940 
1941  resultSet = statement.executeQuery("SELECT * from tsk_event_types");
1942 
1943  // If there is something in resultSet then the table must have previously
1944  // existing therefore there is not need to populate
1945  if (!resultSet.next()) {
1946 
1947  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1948  + " values( 0, 'Event Types', null)");
1949  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1950  + " values(1, 'File System', 0)");
1951  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1952  + " values(2, 'Web Activity', 0)");
1953  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1954  + " values(3, 'Misc Types', 0)");
1955  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1956  + " values(4, 'Modified', 1)");
1957  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1958  + " values(5, 'Accessed', 1)");
1959  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1960  + " values(6, 'Created', 1)");
1961  statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)"
1962  + " values(7, 'Changed', 1)");
1963  }
1964 
1965  // Delete the old table that may have been created with the upgrade
1966  // from 8.1 to 8.2.
1967  statement.execute("DROP TABLE IF EXISTS tsk_events");
1968 
1969  // Delete the old table that may have been created with the upgrade
1970  // from 8.1 to 8.2
1971  statement.execute("DROP TABLE IF EXISTS tsk_event_descriptions");
1972 
1973  //create new tsk_event_description table
1974  statement.execute("CREATE TABLE tsk_event_descriptions ("
1975  + " event_description_id " + primaryKeyType + " PRIMARY KEY, "
1976  + " full_description TEXT NOT NULL, "
1977  + " med_description TEXT, "
1978  + " short_description TEXT,"
1979  + " data_source_obj_id BIGINT NOT NULL, "
1980  + " file_obj_id BIGINT NOT NULL, "
1981  + " artifact_id BIGINT, "
1982  + " hash_hit INTEGER NOT NULL, " //boolean
1983  + " tagged INTEGER NOT NULL, " //boolean
1984  + " UNIQUE(full_description, file_obj_id, artifact_id), "
1985  + " FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id), "
1986  + " FOREIGN KEY(file_obj_id) REFERENCES tsk_files(obj_id), "
1987  + " FOREIGN KEY(artifact_id) REFERENCES blackboard_artifacts(artifact_id))"
1988  );
1989 
1990  // create a new table
1991  statement.execute("CREATE TABLE tsk_events ( "
1992  + " event_id " + primaryKeyType + " PRIMARY KEY, "
1993  + " event_type_id BIGINT NOT NULL REFERENCES tsk_event_types(event_type_id) ,"
1994  + " event_description_id BIGINT NOT NULL REFERENCES tsk_event_descriptions(event_description_id) ,"
1995  + " time INTEGER NOT NULL, "
1996  + " UNIQUE (event_type_id, event_description_id, time))"
1997  );
1998 
1999  // Fix mistakenly set names in tsk_db_info_extended
2000  statement.execute("UPDATE tsk_db_info_extended SET name = 'CREATION_SCHEMA_MAJOR_VERSION' WHERE name = 'CREATED_SCHEMA_MAJOR_VERSION'");
2001  statement.execute("UPDATE tsk_db_info_extended SET name = 'CREATION_SCHEMA_MINOR_VERSION' WHERE name = 'CREATED_SCHEMA_MINOR_VERSION'");
2002 
2003  return new CaseDbSchemaVersionNumber(8, 3);
2004  } finally {
2005  closeResultSet(resultSet);
2007  }
2008  }
2009 
2031  private CaseDbSchemaVersionNumber updateFromSchema8dot3toSchema8dot4(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
2032  if (schemaVersion.getMajor() != 8) {
2033  return schemaVersion;
2034  }
2035 
2036  if (schemaVersion.getMinor() != 3) {
2037  return schemaVersion;
2038  }
2039 
2040  Statement statement = connection.createStatement();
2041  ResultSet results = null;
2042 
2044  try {
2045  // This is a bug fix update for a misnamed column in tsk_event_descriptions in
2046  // the previous update code.
2047  if (null == getDatabaseType()) {
2048  throw new TskCoreException("Unsupported data base type: " + getDatabaseType().toString());
2049  }
2050 
2051  switch (getDatabaseType()) {
2052  case POSTGRESQL:
2053  // Check if the misnamed column is present
2054  results = statement.executeQuery("SELECT column_name FROM information_schema.columns "
2055  + "WHERE table_name='tsk_event_descriptions' and column_name='file_obj_id'");
2056  if (results.next()) {
2057  // In PostgreSQL we can rename the column if it exists
2058  statement.execute("ALTER TABLE tsk_event_descriptions "
2059  + "RENAME COLUMN file_obj_id TO content_obj_id");
2060 
2061  // In 8.2 to 8.3 upgrade, the event_id & time column in tsk_events table was erroneously created as type INTEGER, instead of BIGINT
2062  // Fix the schema, preserving any data if exists.
2063  statement.execute("CREATE TABLE temp_tsk_events ( "
2064  + " event_id BIGSERIAL PRIMARY KEY, "
2065  + " event_type_id BIGINT NOT NULL REFERENCES tsk_event_types(event_type_id) ,"
2066  + " event_description_id BIGINT NOT NULL REFERENCES tsk_event_descriptions(event_description_id),"
2067  + " time BIGINT NOT NULL, "
2068  + " UNIQUE (event_type_id, event_description_id, time))"
2069  );
2070 
2071  // Copy the data
2072  statement.execute("INSERT INTO temp_tsk_events(event_id, event_type_id, "
2073  + "event_description_id, time) SELECT * FROM tsk_events");
2074 
2075  // Drop the old table
2076  statement.execute("DROP TABLE tsk_events");
2077 
2078  // Rename the new table
2079  statement.execute("ALTER TABLE temp_tsk_events RENAME TO tsk_events");
2080 
2081  //create tsk_events indices that were skipped in the 8.2 to 8.3 update code
2082  statement.execute("CREATE INDEX events_data_source_obj_id ON tsk_event_descriptions(data_source_obj_id) ");
2083  statement.execute("CREATE INDEX events_content_obj_id ON tsk_event_descriptions(content_obj_id) ");
2084  statement.execute("CREATE INDEX events_artifact_id ON tsk_event_descriptions(artifact_id) ");
2085  statement.execute("CREATE INDEX events_sub_type_time ON tsk_events(event_type_id, time) ");
2086  statement.execute("CREATE INDEX events_time ON tsk_events(time) ");
2087  }
2088  break;
2089  case SQLITE:
2090  boolean hasMisnamedColumn = false;
2091  results = statement.executeQuery("pragma table_info('tsk_event_descriptions')");
2092  while (results.next()) {
2093  if (results.getString("name") != null && results.getString("name").equals("file_obj_id")) {
2094  hasMisnamedColumn = true;
2095  break;
2096  }
2097  }
2098 
2099  if (hasMisnamedColumn) {
2100  // Since we can't rename the column we'll need to make new tables and copy the data
2101  statement.execute("CREATE TABLE temp_tsk_event_descriptions ("
2102  + " event_description_id INTEGER PRIMARY KEY, "
2103  + " full_description TEXT NOT NULL, "
2104  + " med_description TEXT, "
2105  + " short_description TEXT,"
2106  + " data_source_obj_id BIGINT NOT NULL, "
2107  + " content_obj_id BIGINT NOT NULL, "
2108  + " artifact_id BIGINT, "
2109  + " hash_hit INTEGER NOT NULL, " //boolean
2110  + " tagged INTEGER NOT NULL, " //boolean
2111  + " UNIQUE(full_description, content_obj_id, artifact_id), "
2112  + " FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id), "
2113  + " FOREIGN KEY(content_obj_id) REFERENCES tsk_files(obj_id), "
2114  + " FOREIGN KEY(artifact_id) REFERENCES blackboard_artifacts(artifact_id))"
2115  );
2116 
2117  statement.execute("CREATE TABLE temp_tsk_events ( "
2118  + " event_id INTEGER PRIMARY KEY, "
2119  + " event_type_id BIGINT NOT NULL REFERENCES tsk_event_types(event_type_id) ,"
2120  + " event_description_id BIGINT NOT NULL REFERENCES temp_tsk_event_descriptions(event_description_id),"
2121  + " time INTEGER NOT NULL, "
2122  + " UNIQUE (event_type_id, event_description_id, time))"
2123  );
2124 
2125  // Copy the data
2126  statement.execute("INSERT INTO temp_tsk_event_descriptions(event_description_id, full_description, "
2127  + "med_description, short_description, data_source_obj_id, content_obj_id, artifact_id, "
2128  + "hash_hit, tagged) SELECT * FROM tsk_event_descriptions");
2129 
2130  statement.execute("INSERT INTO temp_tsk_events(event_id, event_type_id, "
2131  + "event_description_id, time) SELECT * FROM tsk_events");
2132 
2133  // Drop the old tables
2134  statement.execute("DROP TABLE tsk_events");
2135  statement.execute("DROP TABLE tsk_event_descriptions");
2136 
2137  // Rename the new tables
2138  statement.execute("ALTER TABLE temp_tsk_event_descriptions RENAME TO tsk_event_descriptions");
2139  statement.execute("ALTER TABLE temp_tsk_events RENAME TO tsk_events");
2140 
2141  //create tsk_events indices
2142  statement.execute("CREATE INDEX events_data_source_obj_id ON tsk_event_descriptions(data_source_obj_id) ");
2143  statement.execute("CREATE INDEX events_content_obj_id ON tsk_event_descriptions(content_obj_id) ");
2144  statement.execute("CREATE INDEX events_artifact_id ON tsk_event_descriptions(artifact_id) ");
2145  statement.execute("CREATE INDEX events_sub_type_time ON tsk_events(event_type_id, time) ");
2146  statement.execute("CREATE INDEX events_time ON tsk_events(time) ");
2147  }
2148  break;
2149  default:
2150  throw new TskCoreException("Unsupported data base type: " + getDatabaseType().toString());
2151  }
2152 
2153  // create pool info table
2154  if (this.dbType.equals(DbType.SQLITE)) {
2155  statement.execute("CREATE TABLE tsk_pool_info (obj_id INTEGER PRIMARY KEY, pool_type INTEGER NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
2156  } else {
2157  statement.execute("CREATE TABLE tsk_pool_info (obj_id BIGSERIAL PRIMARY KEY, pool_type INTEGER NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)");
2158  }
2159 
2160  // Add new account types for newly supported messaging applications, if they dont exists already.
2161  insertAccountTypeIfNotExists(statement, "IMO", "IMO");
2162  insertAccountTypeIfNotExists(statement, "LINE", "LINE");
2163  insertAccountTypeIfNotExists(statement, "SKYPE", "Skype");
2164  insertAccountTypeIfNotExists(statement, "TANGO", "Tango");
2165  insertAccountTypeIfNotExists(statement, "TEXTNOW", "TextNow");
2166  insertAccountTypeIfNotExists(statement, "THREEMA", "ThreeMa");
2167  insertAccountTypeIfNotExists(statement, "VIBER", "Viber");
2168  insertAccountTypeIfNotExists(statement, "XENDER", "Xender");
2169  insertAccountTypeIfNotExists(statement, "ZAPYA", "Zapya");
2170  insertAccountTypeIfNotExists(statement, "SHAREIT", "ShareIt");
2171 
2172  return new CaseDbSchemaVersionNumber(8, 4);
2173  } finally {
2174  closeResultSet(results);
2175  closeStatement(statement);
2177  }
2178  }
2179 
2180  private CaseDbSchemaVersionNumber updateFromSchema8dot4toSchema8dot5(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
2181  if (schemaVersion.getMajor() != 8) {
2182  return schemaVersion;
2183  }
2184 
2185  if (schemaVersion.getMinor() != 4) {
2186  return schemaVersion;
2187  }
2188 
2189  Statement statement = connection.createStatement();
2191  try {
2192  switch (getDatabaseType()) {
2193  case POSTGRESQL:
2194  statement.execute("CREATE TABLE tsk_tag_sets (tag_set_id BIGSERIAL PRIMARY KEY, name TEXT UNIQUE)");
2195  statement.execute("ALTER TABLE tag_names ADD COLUMN tag_set_id BIGINT REFERENCES tsk_tag_sets(tag_set_id)");
2196  break;
2197  case SQLITE:
2198  statement.execute("CREATE TABLE tsk_tag_sets (tag_set_id INTEGER PRIMARY KEY, name TEXT UNIQUE)");
2199  statement.execute("ALTER TABLE tag_names ADD COLUMN tag_set_id INTEGER REFERENCES tsk_tag_sets(tag_set_id)");
2200  break;
2201  }
2202 
2203  statement.execute("ALTER TABLE tag_names ADD COLUMN rank INTEGER");
2204 
2205  /*
2206  * Update existing Project Vic tag names (from Image Gallery in
2207  * Autopsy) to be part of a Tag Set. NOTE: These names are out of
2208  * date and will not work with the Project VIC Report module. New
2209  * cases will get the new names from Image Gallery.
2210  */
2211  String insertStmt = "INSERT INTO tsk_tag_sets (name) VALUES ('Project VIC')";
2212  if (getDatabaseType() == DbType.POSTGRESQL) {
2213  statement.execute(insertStmt, Statement.RETURN_GENERATED_KEYS);
2214  } else {
2215  statement.execute(insertStmt);
2216  }
2217  try (ResultSet resultSet = statement.getGeneratedKeys()) {
2218  if (resultSet != null && resultSet.next()) {
2219  int tagSetId = resultSet.getInt(1);
2220 
2221  String updateQuery = "UPDATE tag_names SET tag_set_id = %d, color = '%s', rank = %d, display_name = '%s' WHERE display_name = '%s'";
2222  statement.executeUpdate(String.format(updateQuery, tagSetId, "Red", 1, "Child Exploitation (Illegal)", "CAT-1: Child Exploitation (Illegal)"));
2223  statement.executeUpdate(String.format(updateQuery, tagSetId, "Lime", 2, "Child Exploitation (Non-Illegal/Age Difficult)", "CAT-2: Child Exploitation (Non-Illegal/Age Difficult)"));
2224  statement.executeUpdate(String.format(updateQuery, tagSetId, "Yellow", 3, "CGI/Animation (Child Exploitive)", "CAT-3: CGI/Animation (Child Exploitive)"));
2225  statement.executeUpdate(String.format(updateQuery, tagSetId, "Purple", 4, "Exemplar/Comparison (Internal Use Only)", "CAT-4: Exemplar/Comparison (Internal Use Only)"));
2226  statement.executeUpdate(String.format(updateQuery, tagSetId, "Fuchsia", 5, "Non-pertinent", "CAT-5: Non-pertinent"));
2227 
2228  String deleteContentTag = "DELETE FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id from tag_names WHERE display_name LIKE 'CAT-0: Uncategorized')";
2229  String deleteArtifactTag = "DELETE FROM blackboard_artifact_tags WHERE tag_name_id IN (SELECT tag_name_id from tag_names WHERE display_name LIKE 'CAT-0: Uncategorized')";
2230  String deleteCat0 = "DELETE FROM tag_names WHERE display_name = 'CAT-0: Uncategorized'";
2231  statement.executeUpdate(deleteContentTag);
2232  statement.executeUpdate(deleteArtifactTag);
2233  statement.executeUpdate(deleteCat0);
2234 
2235  } else {
2236  throw new TskCoreException("Failed to retrieve the default tag_set_id from DB");
2237  }
2238  }
2239 
2240  // Add data_source_obj_id column to the tsk_files table. For newly created cases
2241  // this column will have a foreign key constraint on the data_source_info table.
2242  // There does not seem to be a reasonable way to do this in an upgrade,
2243  // so upgraded cases will be missing the foreign key.
2244  switch (getDatabaseType()) {
2245  case POSTGRESQL:
2246  statement.execute("ALTER TABLE tsk_fs_info ADD COLUMN data_source_obj_id BIGINT NOT NULL DEFAULT -1;");
2247  break;
2248  case SQLITE:
2249  statement.execute("ALTER TABLE tsk_fs_info ADD COLUMN data_source_obj_id INTEGER NOT NULL DEFAULT -1;");
2250  break;
2251  }
2252  Statement updateStatement = connection.createStatement();
2253  try (ResultSet resultSet = statement.executeQuery("SELECT obj_id FROM tsk_fs_info")) {
2254  while (resultSet.next()) {
2255  long fsId = resultSet.getLong("obj_id");
2256  long dataSourceId = getDataSourceObjectId(connection, fsId);
2257  updateStatement.executeUpdate("UPDATE tsk_fs_info SET data_source_obj_id = " + dataSourceId + " WHERE obj_id = " + fsId + ";");
2258  }
2259  } finally {
2260  closeStatement(updateStatement);
2261  }
2262 
2263  return new CaseDbSchemaVersionNumber(8, 5);
2264 
2265  } finally {
2266  closeStatement(statement);
2268  }
2269  }
2270 
2271  private CaseDbSchemaVersionNumber updateFromSchema8dot5toSchema8dot6(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
2272  if (schemaVersion.getMajor() != 8) {
2273  return schemaVersion;
2274  }
2275 
2276  if (schemaVersion.getMinor() != 5) {
2277  return schemaVersion;
2278  }
2279 
2280  Statement statement = connection.createStatement();
2282  try {
2283  statement.execute("ALTER TABLE tsk_files ADD COLUMN sha256 TEXT");
2284 
2285  return new CaseDbSchemaVersionNumber(8, 6);
2286 
2287  } finally {
2288  closeStatement(statement);
2290  }
2291  }
2292 
2293  @SuppressWarnings("deprecation")
2294  private CaseDbSchemaVersionNumber updateFromSchema8dot6toSchema9dot0(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
2295  if (schemaVersion.getMajor() != 8) {
2296  return schemaVersion;
2297  }
2298 
2299  if (schemaVersion.getMinor() != 6) {
2300  return schemaVersion;
2301  }
2302 
2303  Statement statement = connection.createStatement();
2305  try {
2306  String dateDataType = "BIGINT";
2307  String bigIntDataType = "BIGINT";
2308  String blobDataType = "BYTEA";
2309  String primaryKeyType = "BIGSERIAL";
2310 
2311  if (this.dbType.equals(DbType.SQLITE)) {
2312  dateDataType = "INTEGER";
2313  bigIntDataType = "INTEGER";
2314  blobDataType = "BLOB";
2315  primaryKeyType = "INTEGER";
2316  }
2317  statement.execute("ALTER TABLE data_source_info ADD COLUMN added_date_time " + dateDataType);
2318  statement.execute("ALTER TABLE data_source_info ADD COLUMN acquisition_tool_settings TEXT");
2319  statement.execute("ALTER TABLE data_source_info ADD COLUMN acquisition_tool_name TEXT");
2320  statement.execute("ALTER TABLE data_source_info ADD COLUMN acquisition_tool_version TEXT");
2321 
2322  // Add category type and initialize the types. We use the list of artifact types that
2323  // were categorized as analysis results as of the 8.7 update to ensure consistency in
2324  // case the built-in types change in a later release.
2325  statement.execute("ALTER TABLE blackboard_artifact_types ADD COLUMN category_type INTEGER DEFAULT 0");
2326  String analysisTypeObjIdList
2327  = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ", "
2328  + BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID() + ", "
2329  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() + ", "
2330  + BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() + ", "
2331  + BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID() + ", "
2332  + BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID() + ", "
2333  + BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID() + ", "
2334  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() + ", "
2335  + BlackboardArtifact.ARTIFACT_TYPE.TSK_FACE_DETECTED.getTypeID() + ", "
2336  + BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED.getTypeID() + ", "
2337  + BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID() + ", "
2338  + BlackboardArtifact.ARTIFACT_TYPE.TSK_VERIFICATION_FAILED.getTypeID() + ", "
2339  + BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE.getTypeID() + ", "
2340  + BlackboardArtifact.ARTIFACT_TYPE.TSK_USER_CONTENT_SUSPECTED.getTypeID() + ", "
2341  + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_ACCOUNT_TYPE.getTypeID() + ", "
2342  + BlackboardArtifact.ARTIFACT_TYPE.TSK_YARA_HIT.getTypeID() + ", "
2343  + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CATEGORIZATION.getTypeID();
2344  statement.execute("UPDATE blackboard_artifact_types SET category_type = " + BlackboardArtifact.Category.ANALYSIS_RESULT.getID()
2345  + " WHERE artifact_type_id IN (" + analysisTypeObjIdList + ")");
2346 
2347  // Create tsk file attributes table
2348  statement.execute("CREATE TABLE tsk_file_attributes (id " + primaryKeyType + " PRIMARY KEY, "
2349  + "obj_id " + bigIntDataType + " NOT NULL, "
2350  + "attribute_type_id " + bigIntDataType + " NOT NULL, "
2351  + "value_type INTEGER NOT NULL, value_byte " + blobDataType + ", "
2352  + "value_text TEXT, value_int32 INTEGER, value_int64 " + bigIntDataType + ", value_double NUMERIC(20, 10), "
2353  + "FOREIGN KEY(obj_id) REFERENCES tsk_files(obj_id) ON DELETE CASCADE, "
2354  + "FOREIGN KEY(attribute_type_id) REFERENCES blackboard_attribute_types(attribute_type_id))");
2355 
2356  // create analysis results tables
2357  statement.execute("CREATE TABLE tsk_analysis_results (artifact_obj_id " + bigIntDataType + " PRIMARY KEY, "
2358  + "conclusion TEXT, "
2359  + "significance INTEGER NOT NULL, "
2360  /*
2361  * method_category was a column in a little distributed
2362  * version of 9.0. It was renamed to priority before public
2363  * release. The 9.1 upgrade code will add the priority
2364  * column. This is commented out since it was never used.
2365  */
2366  // + "method_category INTEGER NOT NULL, "
2367  + "configuration TEXT, justification TEXT, "
2368  + "ignore_score INTEGER DEFAULT 0 " // boolean
2369  + ")");
2370 
2371  statement.execute("CREATE TABLE tsk_aggregate_score( obj_id " + bigIntDataType + " PRIMARY KEY, "
2372  + "data_source_obj_id " + bigIntDataType + ", "
2373  + "significance INTEGER NOT NULL, "
2374  // See comment above on why this is commented out
2375  // + "method_category INTEGER NOT NULL, "
2376  + "UNIQUE (obj_id),"
2377  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
2378  + "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE "
2379  + ")");
2380 
2381  // Create person table.
2382  statement.execute("CREATE TABLE tsk_persons (id " + primaryKeyType + " PRIMARY KEY, "
2383  + "name TEXT NOT NULL, " // person name
2384  + "UNIQUE(name)) ");
2385 
2386  // Create host table.
2387  statement.execute("CREATE TABLE tsk_hosts (id " + primaryKeyType + " PRIMARY KEY, "
2388  + "name TEXT NOT NULL, " // host name
2389  + "db_status INTEGER DEFAULT 0, " // active/merged/deleted
2390  + "person_id INTEGER, "
2391  + "merged_into " + bigIntDataType + ", "
2392  + "FOREIGN KEY(person_id) REFERENCES tsk_persons(id) ON DELETE SET NULL, "
2393  + "FOREIGN KEY(merged_into) REFERENCES tsk_hosts(id), "
2394  + "UNIQUE(name)) ");
2395 
2396  // Create OS Account and related tables
2397  statement.execute("CREATE TABLE tsk_os_account_realms (id " + primaryKeyType + " PRIMARY KEY, "
2398  + "realm_name TEXT DEFAULT NULL, " // realm name - for a domain realm, may be null
2399  + "realm_addr TEXT DEFAULT NULL, " // a sid/uid or some some other identifier, may be null
2400  + "realm_signature TEXT NOT NULL, " // Signature exists only to prevent duplicates. It is made up of realm address/name and scope host
2401  + "scope_host_id " + bigIntDataType + " DEFAULT NULL, " // if the realm scope is a single host
2402  + "scope_confidence INTEGER, " // indicates whether we know for sure the realm scope or if we are inferring it
2403  + "db_status INTEGER DEFAULT 0, " // active/merged/deleted
2404  + "merged_into " + bigIntDataType + " DEFAULT NULL, "
2405  + "UNIQUE(realm_signature), "
2406  + "FOREIGN KEY(scope_host_id) REFERENCES tsk_hosts(id),"
2407  + "FOREIGN KEY(merged_into) REFERENCES tsk_os_account_realms(id) )");
2408 
2409  // Add host column and create a host for each existing data source.
2410  // We will create a host for each device id so that related data sources will
2411  // be associated with the same host.
2412  statement.execute("ALTER TABLE data_source_info ADD COLUMN host_id INTEGER REFERENCES tsk_hosts(id)");
2413  Statement updateStatement = connection.createStatement();
2414  try (ResultSet resultSet = statement.executeQuery("SELECT obj_id, device_id FROM data_source_info")) {
2415  Map<String, Long> hostMap = new HashMap<>();
2416  long hostIndex = 1;
2417  while (resultSet.next()) {
2418  long objId = resultSet.getLong("obj_id");
2419  String deviceId = resultSet.getString("device_id");
2420 
2421  if (!hostMap.containsKey(deviceId)) {
2422  String hostName = "Host " + hostIndex;
2423  updateStatement.execute("INSERT INTO tsk_hosts (name, db_status) VALUES ('" + hostName + "', 0)");
2424  hostMap.put(deviceId, hostIndex);
2425  hostIndex++;
2426  }
2427  updateStatement.execute("UPDATE data_source_info SET host_id = " + hostMap.get(deviceId) + " WHERE obj_id = " + objId);
2428  }
2429  } finally {
2430  closeStatement(updateStatement);
2431  }
2432 
2433  statement.execute("CREATE TABLE tsk_os_accounts (os_account_obj_id " + bigIntDataType + " PRIMARY KEY, "
2434  + "login_name TEXT DEFAULT NULL, " // login name, if available, may be null
2435  + "full_name TEXT DEFAULT NULL, " // full name, if available, may be null
2436  + "realm_id " + bigIntDataType + " NOT NULL, " // realm for the account
2437  + "addr TEXT DEFAULT NULL, " // SID/UID, if available
2438  + "signature TEXT NOT NULL, " // This exists only to prevent duplicates. It is either the addr or the login_name whichever is not null.
2439  + "status INTEGER, " // enabled/disabled/deleted
2440  + "type INTEGER, " // service/interactive
2441  + "created_date " + bigIntDataType + " DEFAULT NULL, "
2442  + "db_status INTEGER DEFAULT 0, " // active/merged/deleted
2443  + "merged_into " + bigIntDataType + " DEFAULT NULL, "
2444  + "UNIQUE(signature, realm_id), "
2445  + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
2446  + "FOREIGN KEY(realm_id) REFERENCES tsk_os_account_realms(id),"
2447  + "FOREIGN KEY(merged_into) REFERENCES tsk_os_accounts(os_account_obj_id) )");
2448 
2449  statement.execute("CREATE TABLE tsk_os_account_attributes (id " + primaryKeyType + " PRIMARY KEY, "
2450  + "os_account_obj_id " + bigIntDataType + " NOT NULL, "
2451  + "host_id " + bigIntDataType + ", "
2452  + "source_obj_id " + bigIntDataType + ", "
2453  + "attribute_type_id " + bigIntDataType + " NOT NULL, "
2454  + "value_type INTEGER NOT NULL, "
2455  + "value_byte " + bigIntDataType + ", "
2456  + "value_text TEXT, "
2457  + "value_int32 INTEGER, value_int64 " + bigIntDataType + ", "
2458  + "value_double NUMERIC(20, 10), "
2459  + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id), "
2460  + "FOREIGN KEY(host_id) REFERENCES tsk_hosts(id), "
2461  + "FOREIGN KEY(source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE SET NULL, "
2462  + "FOREIGN KEY(attribute_type_id) REFERENCES blackboard_attribute_types(attribute_type_id))");
2463 
2464  statement.execute("CREATE TABLE tsk_os_account_instances (id " + primaryKeyType + " PRIMARY KEY, "
2465  + "os_account_obj_id " + bigIntDataType + " NOT NULL, "
2466  + "data_source_obj_id " + bigIntDataType + " NOT NULL, "
2467  + "instance_type INTEGER NOT NULL, " // PerformedActionOn/ReferencedOn
2468  + "UNIQUE(os_account_obj_id, data_source_obj_id), "
2469  + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id), "
2470  + "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE )");
2471 
2472  statement.execute("CREATE TABLE tsk_data_artifacts ( "
2473  + "artifact_obj_id " + bigIntDataType + " PRIMARY KEY, "
2474  + "os_account_obj_id " + bigIntDataType + ", "
2475  + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id)) ");
2476 
2477  // add owner_uid & os_account_obj_id columns to tsk_files
2478  statement.execute("ALTER TABLE tsk_files ADD COLUMN owner_uid TEXT DEFAULT NULL");
2479  statement.execute("ALTER TABLE tsk_files ADD COLUMN os_account_obj_id " + bigIntDataType + " DEFAULT NULL REFERENCES tsk_os_accounts(os_account_obj_id) ");
2480 
2481  // create host address tables
2482  statement.execute("CREATE TABLE tsk_host_addresses (id " + primaryKeyType + " PRIMARY KEY, "
2483  + "address_type INTEGER NOT NULL, "
2484  + "address TEXT NOT NULL, "
2485  + "UNIQUE(address_type, address)) ");
2486 
2487  statement.execute("CREATE TABLE tsk_host_address_map (id " + primaryKeyType + " PRIMARY KEY, "
2488  + "host_id " + bigIntDataType + " NOT NULL, "
2489  + "addr_obj_id " + bigIntDataType + " NOT NULL, "
2490  + "source_obj_id " + bigIntDataType + ", " // object id of the source where this mapping was found.
2491  + "time " + bigIntDataType + ", " // time at which the mapping existed
2492  + "UNIQUE(host_id, addr_obj_id, time), "
2493  + "FOREIGN KEY(host_id) REFERENCES tsk_hosts(id) ON DELETE CASCADE, "
2494  + "FOREIGN KEY(addr_obj_id) REFERENCES tsk_host_addresses(id), "
2495  + "FOREIGN KEY(source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE SET NULL )");
2496 
2497  // stores associations between DNS name and IP address
2498  statement.execute("CREATE TABLE tsk_host_address_dns_ip_map (id " + primaryKeyType + " PRIMARY KEY, "
2499  + "dns_address_id " + bigIntDataType + " NOT NULL, "
2500  + "ip_address_id " + bigIntDataType + " NOT NULL, "
2501  + "source_obj_id " + bigIntDataType + ", "
2502  + "time " + bigIntDataType + ", " // time at which the mapping existed
2503  + "UNIQUE(dns_address_id, ip_address_id, time), "
2504  + "FOREIGN KEY(dns_address_id) REFERENCES tsk_host_addresses(id) ON DELETE CASCADE, "
2505  + "FOREIGN KEY(ip_address_id) REFERENCES tsk_host_addresses(id) ON DELETE CASCADE,"
2506  + "FOREIGN KEY(source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE SET NULL )");
2507 
2508  // maps an address to an artifact using it
2509  statement.execute("CREATE TABLE tsk_host_address_usage (id " + primaryKeyType + " PRIMARY KEY, "
2510  + "addr_obj_id " + bigIntDataType + " NOT NULL, "
2511  + "obj_id " + bigIntDataType + " NOT NULL, "
2512  + "data_source_obj_id " + bigIntDataType + " NOT NULL, " // data source where the usage was found
2513  + "UNIQUE(addr_obj_id, obj_id), "
2514  + "FOREIGN KEY(addr_obj_id) REFERENCES tsk_host_addresses(id) ON DELETE CASCADE, "
2515  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE )");
2516 
2517  return new CaseDbSchemaVersionNumber(9, 0);
2518 
2519  } finally {
2520  closeStatement(statement);
2522  }
2523  }
2524 
2525  private CaseDbSchemaVersionNumber updateFromSchema9dot0toSchema9dot1(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
2526  if (schemaVersion.getMajor() != 9) {
2527  return schemaVersion;
2528  }
2529 
2530  if (schemaVersion.getMinor() != 0) {
2531  return schemaVersion;
2532  }
2533 
2534  Statement statement = connection.createStatement();
2535  ResultSet results = null;
2537  try {
2538  // The 9.0 schema contained method_category columns that were renamed to priority.
2539  switch (getDatabaseType()) {
2540  case POSTGRESQL:
2541  // Check if the misnamed column is present. We'll assume here that the column will exist
2542  // in both tables if present in one.
2543  results = statement.executeQuery("SELECT column_name FROM information_schema.columns "
2544  + "WHERE table_name='tsk_analysis_results' and column_name='method_category'");
2545  if (results.next()) {
2546  // In PostgreSQL we can delete the column
2547  statement.execute("ALTER TABLE tsk_analysis_results "
2548  + "DROP COLUMN method_category");
2549  statement.execute("ALTER TABLE tsk_aggregate_score "
2550  + "DROP COLUMN method_category");
2551  }
2552  break;
2553  case SQLITE:
2554  // Check if the misnamed column is present. We'll assume here that the column will exist
2555  // in both tables if present in one.
2556  boolean hasMisnamedColumn = false;
2557  results = statement.executeQuery("pragma table_info('tsk_analysis_results')");
2558  while (results.next()) {
2559  if (results.getString("name") != null && results.getString("name").equals("method_category")) {
2560  hasMisnamedColumn = true;
2561  break;
2562  }
2563  }
2564 
2565  if (hasMisnamedColumn) {
2566  // Since we can't rename the column we'll need to make a new table and copy the data.
2567  // We'll add the priority column later.
2568  statement.execute("CREATE TABLE temp_tsk_analysis_results (artifact_obj_id INTEGER PRIMARY KEY, "
2569  + "conclusion TEXT, "
2570  + "significance INTEGER NOT NULL, "
2571  + "configuration TEXT, justification TEXT, "
2572  + "ignore_score INTEGER DEFAULT 0 " // boolean
2573  + ")");
2574  statement.execute("CREATE TABLE temp_tsk_aggregate_score( obj_id INTEGER PRIMARY KEY, "
2575  + "data_source_obj_id INTEGER, "
2576  + "significance INTEGER NOT NULL, "
2577  + "UNIQUE (obj_id),"
2578  + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, "
2579  + "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE "
2580  + ")");
2581 
2582  // Copy the data
2583  statement.execute("INSERT INTO temp_tsk_analysis_results(artifact_obj_id, "
2584  + "conclusion, justification, significance, configuration, ignore_score) "
2585  + "SELECT artifact_obj_id, conclusion, justification, significance, configuration, ignore_score FROM tsk_analysis_results");
2586  statement.execute("INSERT INTO temp_tsk_aggregate_score(obj_id, "
2587  + "data_source_obj_id, significance) "
2588  + "SELECT obj_id, data_source_obj_id, significance FROM tsk_aggregate_score");
2589 
2590  // Drop the old tables
2591  statement.execute("DROP TABLE tsk_analysis_results");
2592  statement.execute("DROP TABLE tsk_aggregate_score");
2593 
2594  // Rename the new tables
2595  statement.execute("ALTER TABLE temp_tsk_analysis_results RENAME TO tsk_analysis_results");
2596  statement.execute("ALTER TABLE temp_tsk_aggregate_score RENAME TO tsk_aggregate_score");
2597 
2598  }
2599  break;
2600  default:
2601  throw new TskCoreException("Unsupported database type: " + getDatabaseType().toString());
2602  }
2603 
2604  // add an index on tsk_file_attributes table.
2605  statement.execute("CREATE INDEX tsk_file_attributes_obj_id ON tsk_file_attributes(obj_id)");
2606 
2607  statement.execute("ALTER TABLE tsk_analysis_results ADD COLUMN priority INTEGER NOT NULL DEFAULT " + Score.Priority.NORMAL.getId());
2608  statement.execute("ALTER TABLE tsk_aggregate_score ADD COLUMN priority INTEGER NOT NULL DEFAULT " + Score.Priority.NORMAL.getId());
2609 
2610  statement.execute("UPDATE blackboard_artifact_types SET category_type = 1 WHERE artifact_type_id = 16");
2611 
2612  return new CaseDbSchemaVersionNumber(9, 1);
2613  } finally {
2614  closeResultSet(results);
2615  closeStatement(statement);
2617  }
2618  }
2619 
2633  private CaseDbSchemaVersionNumber updateFromSchema9dot1toSchema9dot2(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
2634  if (schemaVersion.getMajor() != 9) {
2635  return schemaVersion;
2636  }
2637 
2638  if (schemaVersion.getMinor() != 1) {
2639  return schemaVersion;
2640  }
2641 
2642  Statement updateSchemaStatement = connection.createStatement();
2643  ResultSet results = null;
2645  try {
2646 
2647  String bigIntDataType = "BIGINT";
2648  String primaryKeyType = "BIGSERIAL";
2649 
2650  if (this.dbType.equals(DbType.SQLITE)) {
2651  bigIntDataType = "INTEGER";
2652  primaryKeyType = "INTEGER";
2653  }
2654 
2655  // In 9.2 we modified the UNIQUE constraint on tsk_os_account_instances to include instance_type column.
2656  // Since SQLite does not allow to drop or alter constraints, we will create a new table, copy the data and delete the old table.
2657  // Rename existing table
2658  updateSchemaStatement.execute("ALTER TABLE tsk_os_account_instances RENAME TO old_tsk_os_account_instances");
2659 
2660  // New table
2661  updateSchemaStatement.execute("CREATE TABLE tsk_os_account_instances (id " + primaryKeyType + " PRIMARY KEY, "
2662  + "os_account_obj_id " + bigIntDataType + " NOT NULL, "
2663  + "data_source_obj_id " + bigIntDataType + " NOT NULL, "
2664  + "instance_type INTEGER NOT NULL, " // PerformedActionOn/ReferencedOn
2665  + "UNIQUE(os_account_obj_id, data_source_obj_id, instance_type), "
2666  + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE CASCADE, "
2667  + "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE ) ");
2668 
2669  // Copy the data from old table, order by id preserves the primary key.
2670  updateSchemaStatement.execute("INSERT INTO tsk_os_account_instances(os_account_obj_id, "
2671  + "data_source_obj_id, instance_type) SELECT os_account_obj_id, data_source_obj_id, instance_type FROM old_tsk_os_account_instances ORDER BY id ASC");
2672 
2673  // delete old table
2674  updateSchemaStatement.execute("DROP TABLE old_tsk_os_account_instances");
2675 
2676  return new CaseDbSchemaVersionNumber(9, 2);
2677  } finally {
2678  closeResultSet(results);
2679  closeStatement(updateSchemaStatement);
2681  }
2682  }
2683 
2684  private CaseDbSchemaVersionNumber updateFromSchema9dot2toSchema9dot3(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException {
2685  if (schemaVersion.getMajor() != 9) {
2686  return schemaVersion;
2687  }
2688 
2689  if (schemaVersion.getMinor() != 2) {
2690  return schemaVersion;
2691  }
2692 
2693  Statement statement = connection.createStatement();
2695  try {
2696  // add a new column 'sha1' to tsk_files
2697  statement.execute("ALTER TABLE tsk_files ADD COLUMN sha1 TEXT");
2698 
2699 
2700  return new CaseDbSchemaVersionNumber(9, 3);
2701 
2702  } finally {
2703  closeStatement(statement);
2705  }
2706  }
2707 
2719  private void insertAccountTypeIfNotExists(Statement statement, String type_name, String display_name) throws TskCoreException, SQLException {
2720 
2721  String insertSQL = String.format("INTO account_types(type_name, display_name) VALUES ('%s', '%s')", type_name, display_name);
2722  switch (getDatabaseType()) {
2723  case POSTGRESQL:
2724  insertSQL = "INSERT " + insertSQL + " ON CONFLICT DO NOTHING"; //NON-NLS
2725  break;
2726  case SQLITE:
2727  insertSQL = "INSERT OR IGNORE " + insertSQL;
2728  break;
2729  default:
2730  throw new TskCoreException("Unknown DB Type: " + getDatabaseType().name());
2731  }
2732  statement.execute(insertSQL); //NON-NLS
2733  }
2734 
2742  static String extractExtension(final String fileName) {
2743  String ext;
2744  int i = fileName.lastIndexOf(".");
2745  // > 0 because we assume it's not an extension if period is the first character
2746  if ((i > 0) && ((i + 1) < fileName.length())) {
2747  ext = fileName.substring(i + 1);
2748  } else {
2749  return "";
2750  }
2751  // we added this at one point to deal with files that had crazy names based on URLs
2752  // it's too hard though to clean those up and not mess up basic extensions though.
2753  // We need to add '-' to the below if we use it again
2754  // String[] findNonAlphanumeric = ext.split("[^a-zA-Z0-9_]");
2755  // if (findNonAlphanumeric.length > 1) {
2756  // ext = findNonAlphanumeric[0];
2757  // }
2758  return ext.toLowerCase();
2759  }
2760 
2771  @Deprecated
2772  public int getSchemaVersion() {
2773  return getDBSchemaVersion().getMajor();
2774  }
2775 
2782  return CURRENT_DB_SCHEMA_VERSION;
2783  }
2784 
2792  return caseDBSchemaCreationVersion;
2793  }
2794 
2801  return this.dbType;
2802  }
2803 
2810  public String getBackupDatabasePath() {
2811  return dbBackupPath;
2812  }
2813 
2828  public CaseDbTransaction beginTransaction() throws TskCoreException {
2829  return new CaseDbTransaction(this);
2830  }
2831 
2837  public String getDatabaseName() {
2838  return databaseName;
2839  }
2840 
2847  public String getDbDirPath() {
2848  return caseDirPath;
2849  }
2850 
2857  if (dbType == DbType.SQLITE) {
2858  rwLock.writeLock().lock();
2859  }
2860  }
2861 
2868  if (dbType == DbType.SQLITE) {
2869  rwLock.writeLock().unlock();
2870  }
2871  }
2872 
2879  if (dbType == DbType.SQLITE) {
2880  rwLock.readLock().lock();
2881  }
2882  }
2883 
2890  if (dbType == DbType.SQLITE) {
2891  rwLock.readLock().unlock();
2892  }
2893  }
2894 
2904  public static SleuthkitCase openCase(String dbPath) throws TskCoreException {
2905  try {
2906  final SleuthkitJNI.CaseDbHandle caseHandle = SleuthkitJNI.openCaseDb(dbPath);
2907  return new SleuthkitCase(dbPath, caseHandle, DbType.SQLITE);
2909  //don't wrap in new TskCoreException
2910  throw ex;
2911  } catch (Exception ex) {
2912  throw new TskCoreException("Failed to open case database at " + dbPath, ex);
2913  }
2914  }
2915 
2927  public static SleuthkitCase openCase(String databaseName, CaseDbConnectionInfo info, String caseDir) throws TskCoreException {
2928  try {
2929  /*
2930  * The flow of this method involves trying to open case and if
2931  * successful, return that case. If unsuccessful, an exception is
2932  * thrown. We catch any exceptions, and use tryConnect() to attempt
2933  * to obtain further information about the error. If tryConnect() is
2934  * unable to successfully connect, tryConnect() will throw a
2935  * TskCoreException with a message containing user-level error
2936  * reporting. If tryConnect() is able to connect, flow continues and
2937  * we rethrow the original exception obtained from trying to create
2938  * the case. In this way, we obtain more detailed information if we
2939  * are able, but do not lose any information if unable.
2940  */
2941  final SleuthkitJNI.CaseDbHandle caseHandle = SleuthkitJNI.openCaseDb(databaseName, info);
2942  return new SleuthkitCase(info.getHost(), Integer.parseInt(info.getPort()), databaseName, info.getUserName(), info.getPassword(), caseHandle, caseDir, info.getDbType());
2943  } catch (PropertyVetoException exp) {
2944  // In this case, the JDBC driver doesn't support PostgreSQL. Use the generic message here.
2945  throw new TskCoreException(exp.getMessage(), exp);
2947  //don't wrap in new TskCoreException
2948  throw ex;
2949  } catch (Exception exp) {
2950  tryConnect(info); // attempt to connect, throw with user-friendly message if unable
2951  throw new TskCoreException(exp.getMessage(), exp); // throw with generic message if tryConnect() was successful
2952  }
2953  }
2954 
2964  public static SleuthkitCase newCase(String dbPath) throws TskCoreException {
2965  try {
2966  CaseDatabaseFactory factory = new CaseDatabaseFactory(dbPath);
2967  factory.createCaseDatabase();
2968 
2969  SleuthkitJNI.CaseDbHandle caseHandle = SleuthkitJNI.openCaseDb(dbPath);
2970  return new SleuthkitCase(dbPath, caseHandle, DbType.SQLITE);
2971  } catch (Exception ex) {
2972  throw new TskCoreException("Failed to create case database at " + dbPath, ex);
2973  }
2974  }
2975 
2991  public static SleuthkitCase newCase(String caseName, CaseDbConnectionInfo info, String caseDirPath) throws TskCoreException {
2992  String databaseName = createCaseDataBaseName(caseName);
2993  try {
3006  CaseDatabaseFactory factory = new CaseDatabaseFactory(databaseName, info);
3007  factory.createCaseDatabase();
3008 
3009  final SleuthkitJNI.CaseDbHandle caseHandle = SleuthkitJNI.openCaseDb(databaseName, info);
3010  return new SleuthkitCase(info.getHost(), Integer.parseInt(info.getPort()),
3011  databaseName, info.getUserName(), info.getPassword(), caseHandle, caseDirPath, info.getDbType());
3012  } catch (PropertyVetoException exp) {
3013  // In this case, the JDBC driver doesn't support PostgreSQL. Use the generic message here.
3014  throw new TskCoreException(exp.getMessage(), exp);
3015  } catch (Exception exp) {
3016  tryConnect(info); // attempt to connect, throw with user-friendly message if unable
3017  throw new TskCoreException(exp.getMessage(), exp); // throw with generic message if tryConnect() was successful
3018  }
3019  }
3020 
3030  private static String createCaseDataBaseName(String candidateDbName) {
3031  String dbName;
3032  if (!candidateDbName.isEmpty()) {
3033  /*
3034  * Replace all non-ASCII characters.
3035  */
3036  dbName = candidateDbName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS
3037 
3038  /*
3039  * Replace all control characters.
3040  */
3041  dbName = dbName.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS
3042 
3043  /*
3044  * Replace /, \, :, ?, space, ' ".
3045  */
3046  dbName = dbName.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS
3047 
3048  /*
3049  * Make it all lowercase.
3050  */
3051  dbName = dbName.toLowerCase();
3052 
3053  /*
3054  * Must start with letter or underscore. If not, prepend an
3055  * underscore.
3056  */
3057  if ((dbName.length() > 0 && !(Character.isLetter(dbName.codePointAt(0))) && !(dbName.codePointAt(0) == '_'))) {
3058  dbName = "_" + dbName;
3059  }
3060 
3061  /*
3062  * Truncate to 63 - 16 = 47 chars to accomodate a timestamp for
3063  * uniqueness.
3064  */
3065  if (dbName.length() > MAX_DB_NAME_LEN_BEFORE_TIMESTAMP) {
3066  dbName = dbName.substring(0, MAX_DB_NAME_LEN_BEFORE_TIMESTAMP);
3067  }
3068 
3069  } else {
3070  /*
3071  * Must start with letter or underscore.
3072  */
3073  dbName = "_";
3074  }
3075  /*
3076  * Add the time stmap.
3077  */
3078  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
3079  Date date = new Date();
3080  dbName = dbName + "_" + dateFormat.format(date);
3081 
3082  return dbName;
3083  }
3084 
3090  @Beta
3092  timelineEventsDisabled.set(true);
3093  }
3094 
3102  public Examiner getCurrentExaminer() throws TskCoreException {
3103 
3104  // return cached value if there's one
3105  if (cachedCurrentExaminer != null) {
3106  return cachedCurrentExaminer;
3107  }
3108  String loginName = System.getProperty("user.name");
3109  if (loginName == null || loginName.isEmpty()) {
3110  throw new TskCoreException("Failed to determine logged in user name.");
3111  }
3112 
3113  ResultSet resultSet = null;
3114  CaseDbConnection connection = null;
3116  try {
3117  connection = connections.getConnection();
3118  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_EXAMINER_BY_LOGIN_NAME);
3119  statement.clearParameters();
3120  statement.setString(1, loginName);
3121  resultSet = connection.executeQuery(statement);
3122  if (resultSet.next()) {
3123  cachedCurrentExaminer = new Examiner(resultSet.getLong("examiner_id"), resultSet.getString("login_name"), resultSet.getString("display_name"));
3124  return cachedCurrentExaminer;
3125  } else {
3126  throw new TskCoreException("Error getting examaminer for name = " + loginName);
3127  }
3128 
3129  } catch (SQLException ex) {
3130  throw new TskCoreException("Error getting examaminer for name = " + loginName, ex);
3131  } finally {
3132  closeResultSet(resultSet);
3133  closeConnection(connection);
3135  }
3136 
3137  }
3138 
3148  Examiner getExaminerById(long id) throws TskCoreException {
3149 
3150  CaseDbConnection connection = null;
3151  ResultSet resultSet = null;
3153  try {
3154  connection = connections.getConnection();
3155  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_EXAMINER_BY_ID);
3156  statement.clearParameters();
3157  statement.setLong(1, id);
3158  resultSet = connection.executeQuery(statement);
3159  if (resultSet.next()) {
3160  return new Examiner(resultSet.getLong("examiner_id"), resultSet.getString("login_name"), resultSet.getString("full_name"));
3161  } else {
3162  throw new TskCoreException("Error getting examaminer for id = " + id);
3163  }
3164  } catch (SQLException ex) {
3165  throw new TskCoreException("Error getting examaminer for id = " + id, ex);
3166  } finally {
3167  closeResultSet(resultSet);
3168  closeConnection(connection);
3170  }
3171  }
3172 
3190  public AddImageProcess makeAddImageProcess(String timeZone, boolean addUnallocSpace, boolean noFatFsOrphans, String imageCopyPath) {
3191  return this.caseHandle.initAddImageProcess(timeZone, addUnallocSpace, noFatFsOrphans, imageCopyPath, this);
3192  }
3193 
3202  public List<Content> getRootObjects() throws TskCoreException {
3203  CaseDbConnection connection = null;
3204  Statement s = null;
3205  ResultSet rs = null;
3207  try {
3208  connection = connections.getConnection();
3209  s = connection.createStatement();
3210  rs = connection.executeQuery(s, "SELECT obj_id, type FROM tsk_objects " //NON-NLS
3211  + "WHERE par_obj_id IS NULL"); //NON-NLS
3212  Collection<ObjectInfo> infos = new ArrayList<ObjectInfo>();
3213  while (rs.next()) {
3214  infos.add(new ObjectInfo(rs.getLong("obj_id"), ObjectType.valueOf(rs.getShort("type")))); //NON-NLS
3215  }
3216 
3217  List<Content> rootObjs = new ArrayList<Content>();
3218  for (ObjectInfo i : infos) {
3219  if (null != i.type) {
3220  switch (i.type) {
3221  case IMG:
3222  rootObjs.add(getImageById(i.id));
3223  break;
3224  case ABSTRACTFILE:
3225  // Check if virtual dir for local files.
3226  AbstractFile af = getAbstractFileById(i.id);
3227  if (af instanceof VirtualDirectory) {
3228  rootObjs.add(af);
3229  } else {
3230  throw new TskCoreException("Parentless object has wrong type to be a root (ABSTRACTFILE, but not VIRTUAL_DIRECTORY: " + i.type);
3231  }
3232  break;
3233  case REPORT:
3234  break;
3235  case OS_ACCOUNT:
3236  break;
3237  case HOST_ADDRESS:
3238  break;
3239  case UNSUPPORTED:
3240  break;
3241  default:
3242  throw new TskCoreException("Parentless object has wrong type to be a root: " + i.type);
3243  }
3244  }
3245  }
3246  return rootObjs;
3247  } catch (SQLException ex) {
3248  throw new TskCoreException("Error getting root objects", ex);
3249  } finally {
3250  closeResultSet(rs);
3251  closeStatement(s);
3252  closeConnection(connection);
3254  }
3255  }
3256 
3268  List<Long> getDataSourceObjIds(String deviceId) throws TskCoreException {
3269 
3270  // check cached map first
3271  synchronized (deviceIdToDatasourceObjIdMap) {
3272  if (deviceIdToDatasourceObjIdMap.containsKey(deviceId)) {
3273  return new ArrayList<Long>(deviceIdToDatasourceObjIdMap.get(deviceId));
3274  }
3275 
3276  CaseDbConnection connection = null;
3277  Statement s = null;
3278  ResultSet rs = null;
3280  try {
3281  connection = connections.getConnection();
3282  s = connection.createStatement();
3283  rs = connection.executeQuery(s, "SELECT obj_id FROM data_source_info WHERE device_id = '" + deviceId + "'"); //NON-NLS
3284  List<Long> dataSourceObjIds = new ArrayList<Long>();
3285  while (rs.next()) {
3286  dataSourceObjIds.add(rs.getLong("obj_id"));
3287 
3288  // Add to map of deviceID to data_source_obj_id.
3289  long ds_obj_id = rs.getLong("obj_id");
3290  if (deviceIdToDatasourceObjIdMap.containsKey(deviceId)) {
3291  deviceIdToDatasourceObjIdMap.get(deviceId).add(ds_obj_id);
3292  } else {
3293  deviceIdToDatasourceObjIdMap.put(deviceId, new HashSet<Long>(Arrays.asList(ds_obj_id)));
3294  }
3295  }
3296  return dataSourceObjIds;
3297  } catch (SQLException ex) {
3298  throw new TskCoreException("Error getting data sources", ex);
3299  } finally {
3300  closeResultSet(rs);
3301  closeStatement(s);
3302  closeConnection(connection);
3304  }
3305  }
3306  }
3307 
3324  public List<DataSource> getDataSources() throws TskCoreException {
3325  CaseDbConnection connection = null;
3326  Statement statement = null;
3327  ResultSet resultSet = null;
3328  Statement statement2 = null;
3329  ResultSet resultSet2 = null;
3331  try {
3332  connection = connections.getConnection();
3333  statement = connection.createStatement();
3334  statement2 = connection.createStatement();
3335  resultSet = connection.executeQuery(statement,
3336  "SELECT ds.obj_id, ds.device_id, ds.time_zone, img.type, img.ssize, img.size, img.md5, img.sha1, img.sha256, img.display_name "
3337  + "FROM data_source_info AS ds "
3338  + "LEFT JOIN tsk_image_info AS img "
3339  + "ON ds.obj_id = img.obj_id"); //NON-NLS
3340 
3341  List<DataSource> dataSourceList = new ArrayList<DataSource>();
3342  Map<Long, List<String>> imagePathsMap = getImagePaths();
3343 
3344  while (resultSet.next()) {
3345  DataSource dataSource;
3346  Long objectId = resultSet.getLong("obj_id");
3347  String deviceId = resultSet.getString("device_id");
3348  String timezone = resultSet.getString("time_zone");
3349  String type = resultSet.getString("type");
3350 
3351  if (type == null) {
3352  /*
3353  * No data found in 'tsk_image_info', so we build a
3354  * LocalFilesDataSource.
3355  */
3356 
3357  resultSet2 = connection.executeQuery(statement2, "SELECT name FROM tsk_files WHERE tsk_files.obj_id = " + objectId); //NON-NLS
3358  String dsName = (resultSet2.next()) ? resultSet2.getString("name") : "";
3359  resultSet2.close();
3360 
3364  final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue()
3365  | TSK_FS_META_FLAG_ENUM.USED.getValue());
3366  String parentPath = "/"; //NON-NLS
3367  dataSource = new LocalFilesDataSource(this, objectId, objectId, deviceId, dsName, dirType, metaType, dirFlag, metaFlags, timezone, null, null, null, FileKnown.UNKNOWN, parentPath);
3368  } else {
3369  /*
3370  * Data found in 'tsk_image_info', so we build an Image.
3371  */
3372  Long ssize = resultSet.getLong("ssize");
3373  Long size = resultSet.getLong("size");
3374  String md5 = resultSet.getString("md5");
3375  String sha1 = resultSet.getString("sha1");
3376  String sha256 = resultSet.getString("sha256");
3377  String name = resultSet.getString("display_name");
3378 
3379  List<String> imagePaths = imagePathsMap.get(objectId);
3380  if (name == null) {
3381  if (imagePaths.size() > 0) {
3382  String path = imagePaths.get(0);
3383  name = (new java.io.File(path)).getName();
3384  } else {
3385  name = "";
3386  }
3387  }
3388 
3389  dataSource = new Image(this, objectId, Long.valueOf(type), deviceId, ssize, name,
3390  imagePaths.toArray(new String[imagePaths.size()]), timezone, md5, sha1, sha256, size);
3391  }
3392 
3393  dataSourceList.add(dataSource);
3394  }
3395 
3396  return dataSourceList;
3397 
3398  } catch (SQLException ex) {
3399  throw new TskCoreException("Error getting data sources", ex);
3400  } finally {
3401  closeResultSet(resultSet);
3402  closeStatement(statement);
3403  closeResultSet(resultSet2);
3404  closeStatement(statement2);
3405  closeConnection(connection);
3407  }
3408  }
3409 
3429  public DataSource getDataSource(long objectId) throws TskDataException, TskCoreException {
3430  DataSource dataSource = null;
3431  CaseDbConnection connection = null;
3432  Statement statement = null;
3433  ResultSet resultSet = null;
3434  Statement statement2 = null;
3435  ResultSet resultSet2 = null;
3437  try {
3438  connection = connections.getConnection();
3439  statement = connection.createStatement();
3440  statement2 = connection.createStatement();
3441  resultSet = connection.executeQuery(statement,
3442  "SELECT ds.device_id, ds.time_zone, img.type, img.ssize, img.size, img.md5, img.sha1, img.sha256, img.display_name "
3443  + "FROM data_source_info AS ds "
3444  + "LEFT JOIN tsk_image_info AS img "
3445  + "ON ds.obj_id = img.obj_id "
3446  + "WHERE ds.obj_id = " + objectId); //NON-NLS
3447  if (resultSet.next()) {
3448  String deviceId = resultSet.getString("device_id");
3449  String timezone = resultSet.getString("time_zone");
3450  String type = resultSet.getString("type");
3451 
3452  if (type == null) {
3453  /*
3454  * No data found in 'tsk_image_info', so we build an
3455  * LocalFilesDataSource.
3456  */
3457 
3458  resultSet2 = connection.executeQuery(statement2, "SELECT name FROM tsk_files WHERE tsk_files.obj_id = " + objectId); //NON-NLS
3459  String dsName = (resultSet2.next()) ? resultSet2.getString("name") : "";
3460 
3464  final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue()
3465  | TSK_FS_META_FLAG_ENUM.USED.getValue());
3466  String parentPath = "/"; //NON-NLS
3467  dataSource = new LocalFilesDataSource(this, objectId, objectId, deviceId, dsName, dirType, metaType, dirFlag, metaFlags, timezone, null, null, null, FileKnown.UNKNOWN, parentPath);
3468  } else {
3469  /*
3470  * Data found in 'tsk_image_info', so we build an Image.
3471  */
3472  Long ssize = resultSet.getLong("ssize");
3473  Long size = resultSet.getLong("size");
3474  String md5 = resultSet.getString("md5");
3475  String sha1 = resultSet.getString("sha1");
3476  String sha256 = resultSet.getString("sha256");
3477  String name = resultSet.getString("display_name");
3478 
3479  List<String> imagePaths = getImagePathsById(objectId, connection);
3480  if (name == null) {
3481  if (imagePaths.size() > 0) {
3482  String path = imagePaths.get(0);
3483  name = (new java.io.File(path)).getName();
3484  } else {
3485  name = "";
3486  }
3487  }
3488 
3489  dataSource = new Image(this, objectId, Long.valueOf(type), deviceId, ssize, name,
3490  imagePaths.toArray(new String[imagePaths.size()]), timezone, md5, sha1, sha256, size);
3491  }
3492  } else {
3493  throw new TskDataException(String.format("There is no data source with obj_id = %d", objectId));
3494  }
3495  } catch (SQLException ex) {
3496  throw new TskCoreException(String.format("Error getting data source with obj_id = %d", objectId), ex);
3497  } finally {
3498  closeResultSet(resultSet);
3499  closeStatement(statement);
3500  closeResultSet(resultSet2);
3501  closeStatement(statement2);
3502  closeConnection(connection);
3504  }
3505 
3506  return dataSource;
3507  }
3508 
3521  @Deprecated
3522  public ArrayList<BlackboardArtifact> getBlackboardArtifacts(int artifactTypeID) throws TskCoreException {
3523  ArrayList<BlackboardArtifact> artifacts = new ArrayList<>();
3524  artifacts.addAll(blackboard.getArtifactsByType(blackboard.getArtifactType(artifactTypeID)));
3525  return artifacts;
3526  }
3527 
3538  public long getBlackboardArtifactsCount(long objId) throws TskCoreException {
3539  CaseDbConnection connection = null;
3540  ResultSet rs = null;
3542  try {
3543  connection = connections.getConnection();
3544 
3545  // SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE obj_id = ?
3546  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_FROM_SOURCE);
3547  statement.clearParameters();
3548  statement.setLong(1, objId);
3549  rs = connection.executeQuery(statement);
3550  long count = 0;
3551  if (rs.next()) {
3552  count = rs.getLong("count");
3553  }
3554  return count;
3555  } catch (SQLException ex) {
3556  throw new TskCoreException("Error getting number of blackboard artifacts by content", ex);
3557  } finally {
3558  closeResultSet(rs);
3559  closeConnection(connection);
3561  }
3562  }
3563 
3574  public long getBlackboardArtifactsTypeCount(int artifactTypeID) throws TskCoreException {
3575  CaseDbConnection connection = null;
3576  ResultSet rs = null;
3578  try {
3579  connection = connections.getConnection();
3580 
3581  // SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE artifact_type_id = ?
3582  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_OF_TYPE);
3583  statement.clearParameters();
3584  statement.setInt(1, artifactTypeID);
3585  rs = connection.executeQuery(statement);
3586  long count = 0;
3587  if (rs.next()) {
3588  count = rs.getLong("count");
3589  }
3590  return count;
3591  } catch (SQLException ex) {
3592  throw new TskCoreException("Error getting number of blackboard artifacts by type", ex);
3593  } finally {
3594  closeResultSet(rs);
3595  closeConnection(connection);
3597  }
3598  }
3599 
3611  public long getBlackboardArtifactsTypeCount(int artifactTypeID, long dataSourceID) throws TskCoreException {
3612  CaseDbConnection connection = null;
3613  ResultSet rs = null;
3615  try {
3616  connection = connections.getConnection();
3617 
3618  // SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE artifact_type_id = ?
3619  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_OF_TYPE_BY_DATA_SOURCE);
3620  statement.clearParameters();
3621  statement.setInt(2, artifactTypeID);
3622  statement.setLong(1, dataSourceID);
3623  rs = connection.executeQuery(statement);
3624  long count = 0;
3625  if (rs.next()) {
3626  count = rs.getLong("count");
3627  }
3628  return count;
3629  } catch (SQLException ex) {
3630  throw new TskCoreException(String.format("Error getting number of blackboard artifacts by type (%d) and data source (%d)", artifactTypeID, dataSourceID), ex);
3631  } finally {
3632  closeResultSet(rs);
3633  closeConnection(connection);
3635  }
3636  }
3637 
3654  @Deprecated
3655  public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, String value) throws TskCoreException {
3657  try (CaseDbConnection connection = connections.getConnection(); Statement statement = connection.createStatement();
3658  ResultSet resultSet = connection.executeQuery(statement, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS
3659  + "arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, "
3660  + "types.type_name AS type_name, types.display_name AS display_name, "//NON-NLS
3661  + " arts.review_status_id AS review_status_id " //NON-NLS
3662  + "FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS
3663  + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS
3664  + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS
3665  + " AND attrs.value_text = '" + value + "'"
3666  + " AND types.artifact_type_id=arts.artifact_type_id"
3667  + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());) { //NON-NLS
3668 
3669  List<Long> analysisArtifactObjIds = new ArrayList<>();
3670  List<Long> dataArtifactObjIds = new ArrayList<>();
3671  while (resultSet.next()) {
3672  BlackboardArtifact.Type type = blackboard.getArtifactType(resultSet.getInt("artifact_type_id"));
3673  if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
3674  analysisArtifactObjIds.add(resultSet.getLong("artifact_obj_id"));
3675  } else {
3676  dataArtifactObjIds.add(resultSet.getLong("artifact_obj_id"));
3677  }
3678  }
3679 
3680  ArrayList<BlackboardArtifact> artifacts = new ArrayList<>();
3681  if (!analysisArtifactObjIds.isEmpty()) {
3682  artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.ANALYSIS_RESULT, "artifacts.artifact_obj_id", analysisArtifactObjIds, connection));
3683  }
3684 
3685  if (!dataArtifactObjIds.isEmpty()) {
3686  artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.DATA_ARTIFACT, "artifacts.artifact_obj_id", dataArtifactObjIds, connection));
3687  }
3688  return artifacts;
3689  } catch (SQLException ex) {
3690  throw new TskCoreException("Error getting blackboard artifacts by attribute", ex);
3691  } finally {
3693  }
3694  }
3695 
3714  @Deprecated
3715  public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, String subString, boolean startsWith) throws TskCoreException {
3716  String valSubStr = "%" + subString; //NON-NLS
3717  if (startsWith == false) {
3718  valSubStr += "%"; //NON-NLS
3719  }
3720 
3722  try (CaseDbConnection connection = connections.getConnection(); Statement statement = connection.createStatement();
3723  ResultSet resultSet = connection.executeQuery(statement, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS
3724  + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " //NON-NLS
3725  + " types.type_name AS type_name, types.display_name AS display_name, " //NON-NLS
3726  + " arts.review_status_id AS review_status_id " //NON-NLS
3727  + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS
3728  + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS
3729  + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS
3730  + " AND LOWER(attrs.value_text) LIKE LOWER('" + valSubStr + "')"
3731  + " AND types.artifact_type_id=arts.artifact_type_id "
3732  + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());) {
3733  List<Long> analysisArtifactObjIds = new ArrayList<>();
3734  List<Long> dataArtifactObjIds = new ArrayList<>();
3735  while (resultSet.next()) {
3736  BlackboardArtifact.Type type = blackboard.getArtifactType(resultSet.getInt("artifact_type_id"));
3737  if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
3738  analysisArtifactObjIds.add(resultSet.getLong("artifact_obj_id"));
3739  } else {
3740  dataArtifactObjIds.add(resultSet.getLong("artifact_obj_id"));
3741  }
3742  }
3743 
3744  ArrayList<BlackboardArtifact> artifacts = new ArrayList<>();
3745  if (!analysisArtifactObjIds.isEmpty()) {
3746  artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.ANALYSIS_RESULT, "artifacts.artifact_obj_id", analysisArtifactObjIds, connection));
3747  }
3748 
3749  if (!dataArtifactObjIds.isEmpty()) {
3750  artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.DATA_ARTIFACT, "artifacts.artifact_obj_id", dataArtifactObjIds, connection));
3751  }
3752  return artifacts;
3753  } catch (SQLException ex) {
3754  throw new TskCoreException("Error getting blackboard artifacts by attribute. " + ex.getMessage(), ex);
3755  } finally {
3757  }
3758  }
3759 
3775  @Deprecated
3776  public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, int value) throws TskCoreException {
3778  try (CaseDbConnection connection = connections.getConnection(); Statement statement = connection.createStatement();
3779  ResultSet resultSet = connection.executeQuery(statement, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS
3780  + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, "
3781  + " types.type_name AS type_name, types.display_name AS display_name, "
3782  + " arts.review_status_id AS review_status_id "//NON-NLS
3783  + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS
3784  + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS
3785  + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS
3786  + " AND attrs.value_int32 = " + value //NON-NLS
3787  + " AND types.artifact_type_id=arts.artifact_type_id "
3788  + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());) {
3789  List<Long> analysisArtifactObjIds = new ArrayList<>();
3790  List<Long> dataArtifactObjIds = new ArrayList<>();
3791  while (resultSet.next()) {
3792  BlackboardArtifact.Type type = blackboard.getArtifactType(resultSet.getInt("artifact_type_id"));
3793  if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
3794  analysisArtifactObjIds.add(resultSet.getLong("artifact_obj_id"));
3795  } else {
3796  dataArtifactObjIds.add(resultSet.getLong("artifact_obj_id"));
3797  }
3798  }
3799 
3800  ArrayList<BlackboardArtifact> artifacts = new ArrayList<>();
3801  if (!analysisArtifactObjIds.isEmpty()) {
3802  artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.ANALYSIS_RESULT, "artifacts.artifact_obj_id", analysisArtifactObjIds, connection));
3803  }
3804 
3805  if (!dataArtifactObjIds.isEmpty()) {
3806  artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.DATA_ARTIFACT, "artifacts.artifact_obj_id", dataArtifactObjIds, connection));
3807  }
3808  return artifacts;
3809  } catch (SQLException ex) {
3810  throw new TskCoreException("Error getting blackboard artifacts by attribute", ex);
3811  } finally {
3813  }
3814  }
3815 
3832  @Deprecated
3833  public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, long value) throws TskCoreException {
3835  try (CaseDbConnection connection = connections.getConnection(); Statement statement = connection.createStatement();
3836  ResultSet resultSet = connection.executeQuery(statement, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS
3837  + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, "
3838  + " types.type_name AS type_name, types.display_name AS display_name, "
3839  + " arts.review_status_id AS review_status_id "//NON-NLS
3840  + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS
3841  + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS
3842  + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS
3843  + " AND attrs.value_int64 = " + value //NON-NLS
3844  + " AND types.artifact_type_id=arts.artifact_type_id "
3845  + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());) {
3846  List<Long> analysisArtifactObjIds = new ArrayList<>();
3847  List<Long> dataArtifactObjIds = new ArrayList<>();
3848  while (resultSet.next()) {
3849  BlackboardArtifact.Type type = blackboard.getArtifactType(resultSet.getInt("artifact_type_id"));
3850  if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
3851  analysisArtifactObjIds.add(resultSet.getLong("artifact_obj_id"));
3852  } else {
3853  dataArtifactObjIds.add(resultSet.getLong("artifact_obj_id"));
3854  }
3855  }
3856 
3857  ArrayList<BlackboardArtifact> artifacts = new ArrayList<>();
3858  if (!analysisArtifactObjIds.isEmpty()) {
3859  artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.ANALYSIS_RESULT, "artifacts.artifact_obj_id", analysisArtifactObjIds, connection));
3860  }
3861 
3862  if (!dataArtifactObjIds.isEmpty()) {
3863  artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.DATA_ARTIFACT, "artifacts.artifact_obj_id", dataArtifactObjIds, connection));
3864  }
3865  return artifacts;
3866  } catch (SQLException ex) {
3867  throw new TskCoreException("Error getting blackboard artifacts by attribute. " + ex.getMessage(), ex);
3868  } finally {
3870  }
3871  }
3872 
3889  @Deprecated
3890  public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, double value) throws TskCoreException {
3892  try (CaseDbConnection connection = connections.getConnection(); Statement statement = connection.createStatement();
3893  ResultSet resultSet = connection.executeQuery(statement, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS
3894  + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, "
3895  + " types.type_name AS type_name, types.display_name AS display_name, "
3896  + " arts.review_status_id AS review_status_id "//NON-NLS
3897  + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS
3898  + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS
3899  + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS
3900  + " AND attrs.value_double = " + value //NON-NLS
3901  + " AND types.artifact_type_id=arts.artifact_type_id "
3902  + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());) {
3903  List<Long> analysisArtifactObjIds = new ArrayList<>();
3904  List<Long> dataArtifactObjIds = new ArrayList<>();
3905  while (resultSet.next()) {
3906  BlackboardArtifact.Type type = blackboard.getArtifactType(resultSet.getInt("artifact_type_id"));
3907  if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
3908  analysisArtifactObjIds.add(resultSet.getLong("artifact_obj_id"));
3909  } else {
3910  dataArtifactObjIds.add(resultSet.getLong("artifact_obj_id"));
3911  }
3912  }
3913 
3914  ArrayList<BlackboardArtifact> artifacts = new ArrayList<>();
3915  if (!analysisArtifactObjIds.isEmpty()) {
3916  artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.ANALYSIS_RESULT, "artifacts.artifact_obj_id", analysisArtifactObjIds, connection));
3917  }
3918 
3919  if (!dataArtifactObjIds.isEmpty()) {
3920  artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.DATA_ARTIFACT, "artifacts.artifact_obj_id", dataArtifactObjIds, connection));
3921  }
3922  return artifacts;
3923  } catch (SQLException ex) {
3924  throw new TskCoreException("Error getting blackboard artifacts by attribute", ex);
3925  } finally {
3927  }
3928  }
3929 
3946  @Deprecated
3947  public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, byte value) throws TskCoreException {
3948 
3950  try (CaseDbConnection connection = connections.getConnection(); Statement statement = connection.createStatement();
3951  ResultSet resultSet = connection.executeQuery(statement, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS
3952  + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, "
3953  + " types.type_name AS type_name, types.display_name AS display_name, "
3954  + " arts.review_status_id AS review_status_id "//NON-NLS
3955  + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS
3956  + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS
3957  + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS
3958  + " AND attrs.value_byte = " + value //NON-NLS
3959  + " AND types.artifact_type_id=arts.artifact_type_id "
3960  + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());) {
3961  List<Long> analysisArtifactObjIds = new ArrayList<>();
3962  List<Long> dataArtifactObjIds = new ArrayList<>();
3963  while (resultSet.next()) {
3964  BlackboardArtifact.Type type = blackboard.getArtifactType(resultSet.getInt("artifact_type_id"));
3965  if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
3966  analysisArtifactObjIds.add(resultSet.getLong("artifact_obj_id"));
3967  } else {
3968  dataArtifactObjIds.add(resultSet.getLong("artifact_obj_id"));
3969  }
3970  }
3971 
3972  ArrayList<BlackboardArtifact> artifacts = new ArrayList<>();
3973  if (!analysisArtifactObjIds.isEmpty()) {
3974  artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.ANALYSIS_RESULT, "artifacts.artifact_obj_id", analysisArtifactObjIds, connection));
3975  }
3976 
3977  if (!dataArtifactObjIds.isEmpty()) {
3978  artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.DATA_ARTIFACT, "artifacts.artifact_obj_id", dataArtifactObjIds, connection));
3979  }
3980  return artifacts;
3981  } catch (SQLException ex) {
3982  throw new TskCoreException("Error getting blackboard artifacts by attribute", ex);
3983  } finally {
3985  }
3986  }
3987 
3995  public Iterable<BlackboardArtifact.Type> getArtifactTypes() throws TskCoreException {
3996  CaseDbConnection connection = null;
3997  Statement s = null;
3998  ResultSet rs = null;
4000  try {
4001  connection = connections.getConnection();
4002  s = connection.createStatement();
4003  rs = connection.executeQuery(s, "SELECT artifact_type_id, type_name, display_name, category_type FROM blackboard_artifact_types"); //NON-NLS
4004  ArrayList<BlackboardArtifact.Type> artifactTypes = new ArrayList<BlackboardArtifact.Type>();
4005  while (rs.next()) {
4006  artifactTypes.add(new BlackboardArtifact.Type(rs.getInt("artifact_type_id"),
4007  rs.getString("type_name"), rs.getString("display_name"),
4008  BlackboardArtifact.Category.fromID(rs.getInt("category_type"))));
4009  }
4010  return artifactTypes;
4011  } catch (SQLException ex) {
4012  throw new TskCoreException("Error getting artifact types", ex); //NON-NLS
4013  } finally {
4014  closeResultSet(rs);
4015  closeStatement(s);
4016  closeConnection(connection);
4018  }
4019  }
4020 
4029  public ArrayList<BlackboardArtifact.ARTIFACT_TYPE> getBlackboardArtifactTypesInUse() throws TskCoreException {
4030  String typeIdList = "";
4031  for (int i = 0; i < BlackboardArtifact.ARTIFACT_TYPE.values().length; ++i) {
4032  typeIdList += BlackboardArtifact.ARTIFACT_TYPE.values()[i].getTypeID();
4033  if (i < BlackboardArtifact.ARTIFACT_TYPE.values().length - 1) {
4034  typeIdList += ", ";
4035  }
4036  }
4037  String query = "SELECT DISTINCT artifact_type_id FROM blackboard_artifacts "
4038  + "WHERE artifact_type_id IN (" + typeIdList + ")";
4039  CaseDbConnection connection = null;
4040  Statement s = null;
4041  ResultSet rs = null;
4043  try {
4044  connection = connections.getConnection();
4045  s = connection.createStatement();
4046  rs = connection.executeQuery(s, query);
4047  ArrayList<BlackboardArtifact.ARTIFACT_TYPE> usedArts = new ArrayList<BlackboardArtifact.ARTIFACT_TYPE>();
4048  while (rs.next()) {
4049  usedArts.add(ARTIFACT_TYPE.fromID(rs.getInt("artifact_type_id")));
4050  }
4051  return usedArts;
4052  } catch (SQLException ex) {
4053  throw new TskCoreException("Error getting artifact types in use", ex);
4054  } finally {
4055  closeResultSet(rs);
4056  closeStatement(s);
4057  closeConnection(connection);
4059  }
4060  }
4061 
4072  public List<BlackboardArtifact.Type> getArtifactTypesInUse() throws TskCoreException {
4073  CaseDbConnection connection = null;
4074  Statement s = null;
4075  ResultSet rs = null;
4077  try {
4078  connection = connections.getConnection();
4079  s = connection.createStatement();
4080  rs = connection.executeQuery(s,
4081  "SELECT DISTINCT arts.artifact_type_id AS artifact_type_id, "
4082  + "types.type_name AS type_name, "
4083  + "types.display_name AS display_name, "
4084  + "types.category_type AS category_type "
4085  + "FROM blackboard_artifact_types AS types "
4086  + "INNER JOIN blackboard_artifacts AS arts "
4087  + "ON arts.artifact_type_id = types.artifact_type_id"); //NON-NLS
4088  List<BlackboardArtifact.Type> uniqueArtifactTypes = new ArrayList<BlackboardArtifact.Type>();
4089  while (rs.next()) {
4090  uniqueArtifactTypes.add(new BlackboardArtifact.Type(rs.getInt("artifact_type_id"),
4091  rs.getString("type_name"), rs.getString("display_name"),
4092  BlackboardArtifact.Category.fromID(rs.getInt("category_type"))));
4093  }
4094  return uniqueArtifactTypes;
4095  } catch (SQLException ex) {
4096  throw new TskCoreException("Error getting attribute types", ex);
4097  } finally {
4098  closeResultSet(rs);
4099  closeStatement(s);
4100  closeConnection(connection);
4102  }
4103  }
4104 
4112  public List<BlackboardAttribute.Type> getAttributeTypes() throws TskCoreException {
4113  CaseDbConnection connection = null;
4114  Statement s = null;
4115  ResultSet rs = null;
4117  try {
4118  connection = connections.getConnection();
4119  s = connection.createStatement();
4120  rs = connection.executeQuery(s, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types"); //NON-NLS
4121  ArrayList<BlackboardAttribute.Type> attribute_types = new ArrayList<BlackboardAttribute.Type>();
4122  while (rs.next()) {
4123  attribute_types.add(new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"),
4124  rs.getString("display_name"), TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type"))));
4125  }
4126  return attribute_types;
4127  } catch (SQLException ex) {
4128  throw new TskCoreException("Error getting attribute types", ex);
4129  } finally {
4130  closeResultSet(rs);
4131  closeStatement(s);
4132  closeConnection(connection);
4134  }
4135  }
4136 
4148  public int getBlackboardAttributeTypesCount() throws TskCoreException {
4149  CaseDbConnection connection = null;
4150  Statement s = null;
4151  ResultSet rs = null;
4153  try {
4154  connection = connections.getConnection();
4155  s = connection.createStatement();
4156  rs = connection.executeQuery(s, "SELECT COUNT(*) AS count FROM blackboard_attribute_types"); //NON-NLS
4157  int count = 0;
4158  if (rs.next()) {
4159  count = rs.getInt("count");
4160  }
4161  return count;
4162  } catch (SQLException ex) {
4163  throw new TskCoreException("Error getting number of blackboard artifacts by type", ex);
4164  } finally {
4165  closeResultSet(rs);
4166  closeStatement(s);
4167  closeConnection(connection);
4169  }
4170  }
4171 
4184  private long getArtifactsCountHelper(int artifactTypeID, long obj_id) throws TskCoreException {
4185  CaseDbConnection connection = null;
4186  ResultSet rs = null;
4188  try {
4189  connection = connections.getConnection();
4190 
4191  // SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE obj_id = ? AND artifact_type_id = ?
4192  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_BY_SOURCE_AND_TYPE);
4193  statement.clearParameters();
4194  statement.setLong(1, obj_id);
4195  statement.setInt(2, artifactTypeID);
4196  rs = connection.executeQuery(statement);
4197  long count = 0;
4198  if (rs.next()) {
4199  count = rs.getLong("count");
4200  }
4201  return count;
4202  } catch (SQLException ex) {
4203  throw new TskCoreException("Error getting blackboard artifact count", ex);
4204  } finally {
4205  closeResultSet(rs);
4206  closeConnection(connection);
4208  }
4209  }
4210 
4223  public ArrayList<BlackboardArtifact> getBlackboardArtifacts(String artifactTypeName, long obj_id) throws TskCoreException {
4224  ArrayList<BlackboardArtifact> artifacts = new ArrayList<>();
4225  artifacts.addAll(blackboard.getArtifactsBySourceId(getArtifactType(artifactTypeName), obj_id));
4226  return artifacts;
4227  }
4228 
4241  public ArrayList<BlackboardArtifact> getBlackboardArtifacts(int artifactTypeID, long obj_id) throws TskCoreException {
4242  ArrayList<BlackboardArtifact> artifacts = new ArrayList<>();
4243  artifacts.addAll(blackboard.getArtifactsBySourceId(blackboard.getArtifactType(artifactTypeID), obj_id));
4244  return artifacts;
4245  }
4246 
4259  public ArrayList<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactType, long obj_id) throws TskCoreException {
4260  return getBlackboardArtifacts(artifactType.getTypeID(), obj_id);
4261  }
4262 
4275  public long getBlackboardArtifactsCount(String artifactTypeName, long obj_id) throws TskCoreException {
4276  int artifactTypeID = this.getArtifactType(artifactTypeName).getTypeID();
4277  if (artifactTypeID == -1) {
4278  return 0;
4279  }
4280  return getArtifactsCountHelper(artifactTypeID, obj_id);
4281  }
4282 
4295  public long getBlackboardArtifactsCount(int artifactTypeID, long obj_id) throws TskCoreException {
4296  return getArtifactsCountHelper(artifactTypeID, obj_id);
4297  }
4298 
4311  public long getBlackboardArtifactsCount(ARTIFACT_TYPE artifactType, long obj_id) throws TskCoreException {
4312  return getArtifactsCountHelper(artifactType.getTypeID(), obj_id);
4313  }
4314 
4326  public ArrayList<BlackboardArtifact> getBlackboardArtifacts(String artifactTypeName) throws TskCoreException {
4327  ArrayList<BlackboardArtifact> artifacts = new ArrayList<>();
4328  artifacts.addAll(blackboard.getArtifactsByType(getArtifactType(artifactTypeName)));
4329  return artifacts;
4330  }
4331 
4343  public ArrayList<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactType) throws TskCoreException {
4344  ArrayList<BlackboardArtifact> artifacts = new ArrayList<>();
4345  artifacts.addAll(blackboard.getArtifactsByType(blackboard.getArtifactType(artifactType.getTypeID())));
4346  return artifacts;
4347  }
4348 
4364  @Deprecated
4365  public List<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactType, BlackboardAttribute.ATTRIBUTE_TYPE attrType, String value) throws TskCoreException {
4366 
4367  String dataArtifactJoin = "tsk_data_artifacts AS datarts ON datarts.artifact_obj_id = arts.artifact_obj_id";
4368  String analysisResultJoin = "tsk_analysis_results AS anresult ON anresult.artifact_obj_id = arts.artifact_obj_id";
4369  String dataArtifactColumns = ", datarts.os_account_obj_id AS os_account_obj_id";
4370  String analysResultColumns = ", anresult.conclusion AS conclusion, anresult.significance AS significance, anresult.priority AS priority, anresult.configuration AS configuration, anresult.justification AS justification ";
4371 
4372  String formatQuery = "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS
4373  + "arts.obj_id AS obj_id, arts.artifact_obj_id as artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, "
4374  + "types.type_name AS type_name, types.display_name AS display_name,"
4375  + "arts.review_status_id AS review_status_id %s "//NON-NLS
4376  + "FROM blackboard_artifacts AS arts "
4377  + "JOIN blackboard_attributes AS attrs ON arts.artifact_id = attrs.artifact_id "
4378  + "JOIN blackboard_artifact_types AS types ON types.artifact_type_id = arts.artifact_type_id " //NON-NLS
4379  + "LEFT JOIN %s "
4380  + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS
4381  + "AND attrs.attribute_type_id = %d "
4382  + " AND arts.artifact_type_id = %d "
4383  + " AND attrs.value_text = '%s' " //NON-NLS
4384  + " AND types.artifact_type_id=arts.artifact_type_id "
4385  + " AND arts.review_status_id != %d";
4386 
4387  String query = String.format(formatQuery,
4388  (artifactType.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT ? analysResultColumns : dataArtifactColumns),
4389  (artifactType.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT ? analysisResultJoin : dataArtifactJoin),
4390  attrType.getTypeID(),
4391  artifactType.getTypeID(),
4392  value,
4394 
4396  try (CaseDbConnection connection = connections.getConnection(); Statement s = connection.createStatement(); ResultSet rs = connection.executeQuery(s, query)) {
4397  ArrayList<BlackboardArtifact> artifacts = new ArrayList<>();
4398  while (rs.next()) {
4399  if (artifactType.getCategory() == BlackboardArtifact.Category.DATA_ARTIFACT) {
4400  Long osAccountObjId = rs.getLong("os_account_obj_id");
4401  if (rs.wasNull()) {
4402  osAccountObjId = null;
4403  }
4404 
4405  artifacts.add(new DataArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"),
4406  rs.getLong("artifact_obj_id"),
4407  rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null,
4408  rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"),
4409  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")), osAccountObjId, false));
4410  } else {
4411  artifacts.add(new AnalysisResult(this, rs.getLong("artifact_id"), rs.getLong("obj_id"),
4412  rs.getLong("artifact_obj_id"),
4413  rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null,
4414  rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"),
4415  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")),
4416  new Score(Score.Significance.fromID(rs.getInt("significance")), Score.Priority.fromID(rs.getInt("priority"))),
4417  rs.getString("conclusion"), rs.getString("configuration"), rs.getString("justification")));
4418  }
4419  }
4420  return artifacts;
4421  } catch (SQLException ex) {
4422  throw new TskCoreException("Error getting blackboard artifacts by artifact type and attribute. " + ex.getMessage(), ex);
4423  } finally {
4425  }
4426  }
4427 
4439  public BlackboardArtifact getBlackboardArtifact(long artifactID) throws TskCoreException {
4440  List<DataArtifact> dataArtifacts = blackboard.getDataArtifactsWhere("artifacts.artifact_id = " + artifactID);
4441  if (!dataArtifacts.isEmpty()) {
4442  return dataArtifacts.get(0);
4443  }
4444 
4445  List<AnalysisResult> analysisResults = blackboard.getAnalysisResultsWhere("artifacts.artifact_id = " + artifactID);
4446  if (!analysisResults.isEmpty()) {
4447  return analysisResults.get(0);
4448  }
4449 
4450  throw new TskCoreException("No blackboard artifact with id " + artifactID);
4451  }
4452 
4461  public void addBlackboardAttribute(BlackboardAttribute attr, int artifactTypeId) throws TskCoreException {
4463  try (CaseDbConnection connection = connections.getConnection();) {
4464  addBlackBoardAttribute(attr, artifactTypeId, connection);
4465  } catch (SQLException ex) {
4466  throw new TskCoreException("Error adding blackboard attribute " + attr.toString(), ex);
4467  } finally {
4469  }
4470  }
4471 
4481  public void addBlackboardAttributes(Collection<BlackboardAttribute> attributes, int artifactTypeId) throws TskCoreException {
4482  CaseDbConnection connection = null;
4484  try {
4485  connection = connections.getConnection();
4486  connection.beginTransaction();
4487  for (final BlackboardAttribute attr : attributes) {
4488  addBlackBoardAttribute(attr, artifactTypeId, connection);
4489  }
4490  connection.commitTransaction();
4491  } catch (SQLException ex) {
4492  rollbackTransaction(connection);
4493  throw new TskCoreException("Error adding blackboard attributes", ex);
4494  } finally {
4495  closeConnection(connection);
4497  }
4498  }
4499 
4500  void addBlackBoardAttribute(BlackboardAttribute attr, int artifactTypeId, CaseDbConnection connection) throws SQLException, TskCoreException {
4501  PreparedStatement statement;
4502  switch (attr.getAttributeType().getValueType()) {
4503  case STRING:
4504  case JSON:
4505  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_STRING_ATTRIBUTE);
4506  statement.clearParameters();
4507  statement.setString(7, attr.getValueString());
4508  break;
4509  case BYTE:
4510  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_BYTE_ATTRIBUTE);
4511  statement.clearParameters();
4512  statement.setBytes(7, attr.getValueBytes());
4513  break;
4514  case INTEGER:
4515  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_INT_ATTRIBUTE);
4516  statement.clearParameters();
4517  statement.setInt(7, attr.getValueInt());
4518  break;
4519  case LONG:
4520  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LONG_ATTRIBUTE);
4521  statement.clearParameters();
4522  statement.setLong(7, attr.getValueLong());
4523  break;
4524  case DOUBLE:
4525  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_DOUBLE_ATTRIBUTE);
4526  statement.clearParameters();
4527  statement.setDouble(7, attr.getValueDouble());
4528  break;
4529  case DATETIME:
4530  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LONG_ATTRIBUTE);
4531  statement.clearParameters();
4532  statement.setLong(7, attr.getValueLong());
4533  break;
4534  default:
4535  throw new TskCoreException("Unrecognized artifact attribute value type");
4536  }
4537  statement.setLong(1, attr.getArtifactID());
4538  statement.setInt(2, artifactTypeId);
4539  statement.setString(3, attr.getSourcesCSV());
4540  statement.setString(4, "");
4541  statement.setInt(5, attr.getAttributeType().getTypeID());
4542  statement.setLong(6, attr.getAttributeType().getValueType().getType());
4543  connection.executeUpdate(statement);
4544  }
4545 
4546  void addFileAttribute(Attribute attr, CaseDbConnection connection) throws SQLException, TskCoreException {
4547  PreparedStatement statement;
4548  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE_ATTRIBUTE, Statement.RETURN_GENERATED_KEYS);
4549  statement.clearParameters();
4550 
4551  statement.setLong(1, attr.getAttributeParentId());
4552  statement.setInt(2, attr.getAttributeType().getTypeID());
4553  statement.setLong(3, attr.getAttributeType().getValueType().getType());
4554 
4555  if (attr.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE) {
4556  statement.setBytes(4, attr.getValueBytes());
4557  } else {
4558  statement.setBytes(4, null);
4559  }
4560 
4561  if (attr.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING
4562  || attr.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
4563  statement.setString(5, attr.getValueString());
4564  } else {
4565  statement.setString(5, null);
4566  }
4567  if (attr.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.INTEGER) {
4568  statement.setInt(6, attr.getValueInt());
4569  } else {
4570  statement.setNull(6, java.sql.Types.INTEGER);
4571  }
4572 
4573  if (attr.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME
4574  || attr.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.LONG) {
4575  statement.setLong(7, attr.getValueLong());
4576  } else {
4577  statement.setNull(7, java.sql.Types.BIGINT);
4578  }
4579 
4580  if (attr.getAttributeType().getValueType() == TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE) {
4581  statement.setDouble(8, attr.getValueDouble());
4582  } else {
4583  statement.setNull(8, java.sql.Types.DOUBLE);
4584  }
4585 
4586  connection.executeUpdate(statement);
4587  try (ResultSet resultSet = statement.getGeneratedKeys()) {
4588  if (!resultSet.next()) {
4589  throw new TskCoreException(String.format("Failed to insert file attribute "
4590  + "with id=%d. The expected key was not generated", attr.getId()));
4591  }
4592 
4593  attr.setId(resultSet.getLong(1));
4594  }
4595  }
4596 
4607  String addSourceToArtifactAttribute(BlackboardAttribute attr, String source) throws TskCoreException {
4608  /*
4609  * WARNING: This is a temporary implementation that is not safe and
4610  * denormalizes the case datbase.
4611  *
4612  * TODO (JIRA-2294): Provide a safe and normalized solution to tracking
4613  * the sources of artifact attributes.
4614  */
4615  if (null == source || source.isEmpty()) {
4616  throw new TskCoreException("Attempt to add null or empty source module name to artifact attribute");
4617  }
4618  CaseDbConnection connection = null;
4620  Statement queryStmt = null;
4621  Statement updateStmt = null;
4622  ResultSet result = null;
4623  String newSources = "";
4624  try {
4625  connection = connections.getConnection();
4626  connection.beginTransaction();
4627  String valueClause = "";
4628  BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType = attr.getAttributeType().getValueType();
4629  if (BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE != valueType) {
4630  switch (valueType) {
4631  case STRING:
4632  case JSON:
4633  valueClause = " value_text = '" + escapeSingleQuotes(attr.getValueString()) + "'";
4634  break;
4635  case INTEGER:
4636  valueClause = " value_int32 = " + attr.getValueInt();
4637  break;
4638  case LONG:
4639  case DATETIME:
4640  valueClause = " value_int64 = " + attr.getValueLong();
4641  break;
4642  case DOUBLE:
4643  valueClause = " value_double = " + attr.getValueDouble();
4644  break;
4645  default:
4646  throw new TskCoreException(String.format("Unrecognized value type for attribute %s", attr.getDisplayString()));
4647  }
4648  String query = "SELECT source FROM blackboard_attributes WHERE"
4649  + " artifact_id = " + attr.getArtifactID()
4650  + " AND attribute_type_id = " + attr.getAttributeType().getTypeID()
4651  + " AND value_type = " + attr.getAttributeType().getValueType().getType()
4652  + " AND " + valueClause + ";";
4653  queryStmt = connection.createStatement();
4654  updateStmt = connection.createStatement();
4655  result = connection.executeQuery(queryStmt, query);
4656  } else {
4657  /*
4658  * SELECT source FROM blackboard_attributes WHERE artifact_id =
4659  * ? AND attribute_type_id = ? AND value_type = 4 AND value_byte
4660  * = ?
4661  */
4662  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ATTR_BY_VALUE_BYTE);
4663  statement.clearParameters();
4664  statement.setLong(1, attr.getArtifactID());
4665  statement.setLong(2, attr.getAttributeType().getTypeID());
4666  statement.setBytes(3, attr.getValueBytes());
4667  result = connection.executeQuery(statement);
4668  }
4669  while (result.next()) {
4670  String oldSources = result.getString("source");
4671  if (null != oldSources && !oldSources.isEmpty()) {
4672  Set<String> uniqueSources = new HashSet<String>(Arrays.asList(oldSources.split(",")));
4673  if (!uniqueSources.contains(source)) {
4674  newSources = oldSources + "," + source;
4675  } else {
4676  newSources = oldSources;
4677  }
4678  } else {
4679  newSources = source;
4680  }
4681  if (BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE != valueType) {
4682  String update = "UPDATE blackboard_attributes SET source = '" + newSources + "' WHERE"
4683  + " artifact_id = " + attr.getArtifactID()
4684  + " AND attribute_type_id = " + attr.getAttributeType().getTypeID()
4685  + " AND value_type = " + attr.getAttributeType().getValueType().getType()
4686  + " AND " + valueClause + ";";
4687  connection.executeUpdate(updateStmt, update);
4688  } else {
4689  /*
4690  * UPDATE blackboard_attributes SET source = ? WHERE
4691  * artifact_id = ? AND attribute_type_id = ? AND value_type
4692  * = 4 AND value_byte = ?
4693  */
4694  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.UPDATE_ATTR_BY_VALUE_BYTE);
4695  statement.clearParameters();
4696  statement.setString(1, newSources);
4697  statement.setLong(2, attr.getArtifactID());
4698  statement.setLong(3, attr.getAttributeType().getTypeID());
4699  statement.setBytes(4, attr.getValueBytes());
4700  connection.executeUpdate(statement);
4701  }
4702  }
4703  connection.commitTransaction();
4704  return newSources;
4705  } catch (SQLException ex) {
4706  rollbackTransaction(connection);
4707  throw new TskCoreException(String.format("Error adding source module to attribute %s", attr.getDisplayString()), ex);
4708  } finally {
4709  closeResultSet(result);
4710  closeStatement(updateStmt);
4711  closeStatement(queryStmt);
4712  closeConnection(connection);
4714  }
4715  }
4716 
4731  @Deprecated
4732  public BlackboardAttribute.Type addArtifactAttributeType(String attrTypeString, TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName) throws TskCoreException, TskDataException {
4733  try {
4734  return blackboard.getOrAddAttributeType(attrTypeString, valueType, displayName);
4735  } catch (BlackboardException ex) {
4736  throw new TskCoreException("Error adding artifact type: " + attrTypeString, ex);
4737  }
4738  }
4739 
4751  @Deprecated
4752  public BlackboardAttribute.Type getAttributeType(String attrTypeName) throws TskCoreException {
4753  return blackboard.getAttributeType(attrTypeName);
4754  }
4755 
4767  @Deprecated
4768  public BlackboardArtifact.Type getArtifactType(String artTypeName) throws TskCoreException {
4769  return blackboard.getArtifactType(artTypeName);
4770  }
4771 
4788  @Deprecated
4789  public BlackboardArtifact.Type addBlackboardArtifactType(String artifactTypeName, String displayName) throws TskCoreException, TskDataException {
4790  return addBlackboardArtifactType(artifactTypeName, displayName, BlackboardArtifact.Category.DATA_ARTIFACT);
4791  }
4792 
4808  @Deprecated
4809  BlackboardArtifact.Type addBlackboardArtifactType(String artifactTypeName, String displayName, BlackboardArtifact.Category category) throws TskCoreException, TskDataException {
4810  try {
4811  return blackboard.getOrAddArtifactType(displayName, displayName, category);
4812  } catch (BlackboardException ex) {
4813  throw new TskCoreException("Error getting or adding artifact type with name: " + artifactTypeName, ex);
4814  }
4815  }
4816 
4828  @Deprecated
4829  public ArrayList<BlackboardAttribute> getBlackboardAttributes(final BlackboardArtifact artifact) throws TskCoreException {
4830  return blackboard.getBlackboardAttributes(artifact);
4831  }
4832 
4833 
4846  public ArrayList<BlackboardAttribute> getMatchingAttributes(String whereClause) throws TskCoreException {
4847  CaseDbConnection connection = null;
4848  Statement s = null;
4849  ResultSet rs = null;
4851  try {
4852  connection = connections.getConnection();
4853  s = connection.createStatement();
4854  rs = connection.executeQuery(s, "SELECT blackboard_attributes.artifact_id AS artifact_id, "
4855  + "blackboard_attributes.source AS source, blackboard_attributes.context AS context, "
4856  + "blackboard_attributes.attribute_type_id AS attribute_type_id, "
4857  + "blackboard_attributes.value_type AS value_type, blackboard_attributes.value_byte AS value_byte, "
4858  + "blackboard_attributes.value_text AS value_text, blackboard_attributes.value_int32 AS value_int32, "
4859  + "blackboard_attributes.value_int64 AS value_int64, blackboard_attributes.value_double AS value_double "
4860  + "FROM blackboard_attributes " + whereClause); //NON-NLS
4861  ArrayList<BlackboardAttribute> matches = new ArrayList<>();
4862  while (rs.next()) {
4864  // attribute type is cached, so this does not necessarily call to the db
4865  type = blackboard.getAttributeType(rs.getInt("attribute_type_id"));
4867  rs.getLong("artifact_id"),
4868  type,
4869  rs.getString("source"),
4870  rs.getString("context"),
4871  rs.getInt("value_int32"),
4872  rs.getLong("value_int64"),
4873  rs.getDouble("value_double"),
4874  rs.getString("value_text"),
4875  rs.getBytes("value_byte"), this
4876  );
4877  matches.add(attr);
4878  }
4879  return matches;
4880  } catch (SQLException ex) {
4881  throw new TskCoreException("Error getting attributes using this where clause: " + whereClause, ex);
4882  } finally {
4883  closeResultSet(rs);
4884  closeStatement(s);
4885  closeConnection(connection);
4887  }
4888  }
4889 
4901  public ArrayList<BlackboardArtifact> getMatchingArtifacts(String whereClause) throws TskCoreException {
4902  String query = "SELECT blackboard_artifacts.artifact_id AS artifact_id, "
4903  + "blackboard_artifacts.obj_id AS obj_id, blackboard_artifacts.artifact_obj_id AS artifact_obj_id, blackboard_artifacts.data_source_obj_id AS data_source_obj_id, blackboard_artifacts.artifact_type_id AS artifact_type_id, "
4904  + "blackboard_artifacts.review_status_id AS review_status_id "
4905  + "FROM blackboard_artifacts " + whereClause;
4907  try (CaseDbConnection connection = connections.getConnection(); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(query)) {
4908 
4909  List<Long> analysisArtifactObjIds = new ArrayList<>();
4910  List<Long> dataArtifactObjIds = new ArrayList<>();
4911  while (resultSet.next()) {
4912  BlackboardArtifact.Type type = blackboard.getArtifactType(resultSet.getInt("artifact_type_id"));
4913  if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
4914  analysisArtifactObjIds.add(resultSet.getLong("artifact_obj_id"));
4915  } else {
4916  dataArtifactObjIds.add(resultSet.getLong("artifact_obj_id"));
4917  }
4918  }
4919 
4920  ArrayList<BlackboardArtifact> matches = new ArrayList<>();
4921  if (!analysisArtifactObjIds.isEmpty()) {
4922  matches.addAll(getArtifactsForValues(BlackboardArtifact.Category.ANALYSIS_RESULT, "artifacts.artifact_obj_id", analysisArtifactObjIds, connection));
4923  }
4924 
4925  if (!dataArtifactObjIds.isEmpty()) {
4926  matches.addAll(getArtifactsForValues(BlackboardArtifact.Category.DATA_ARTIFACT, "artifacts.artifact_obj_id", dataArtifactObjIds, connection));
4927  }
4928 
4929  return matches;
4930  } catch (SQLException ex) {
4931  throw new TskCoreException("Error getting attributes using this where clause: " + whereClause, ex);
4932  } finally {
4934  }
4935  }
4936 
4951  @Deprecated
4952  public BlackboardArtifact newBlackboardArtifact(int artifactTypeID, long obj_id) throws TskCoreException {
4953  BlackboardArtifact.Type type = blackboard.getArtifactType(artifactTypeID);
4954  if (type == null) {
4955  throw new TskCoreException("Unknown artifact type for id: " + artifactTypeID);
4956  }
4957 
4958  Category category = type.getCategory();
4959  if (category == null) {
4960  throw new TskCoreException(String.format("No category for %s (id: %d)",
4961  type.getDisplayName() == null ? "<null>" : type.getDisplayName(),
4962  type.getTypeID()));
4963  }
4964 
4965  Content content = getContentById(obj_id);
4966  if (content == null) {
4967  throw new TskCoreException("No content found for object id: " + obj_id);
4968  }
4969 
4970  switch (category) {
4971  case ANALYSIS_RESULT:
4972  return content.newAnalysisResult(type, Score.SCORE_UNKNOWN, null, null, null, Collections.emptyList())
4973  .getAnalysisResult();
4974  case DATA_ARTIFACT:
4975  return content.newDataArtifact(type, Collections.emptyList());
4976  default:
4977  throw new TskCoreException("Unknown category type: " + category.getName());
4978  }
4979  }
4980 
4993  @Deprecated
4994  @SuppressWarnings("deprecation")
4995  public BlackboardArtifact newBlackboardArtifact(ARTIFACT_TYPE artifactType, long obj_id) throws TskCoreException {
4996  return newBlackboardArtifact(artifactType.getTypeID(), obj_id);
4997  }
4998 
5014  @Deprecated
5015  @SuppressWarnings("deprecation")
5016  BlackboardArtifact newBlackboardArtifact(int artifactTypeID, long obj_id, long data_source_obj_id) throws TskCoreException {
5017  BlackboardArtifact.Type type = blackboard.getArtifactType(artifactTypeID);
5018  try (CaseDbConnection connection = connections.getConnection()) {
5019  return newBlackboardArtifact(artifactTypeID, obj_id, type.getTypeName(), type.getDisplayName(), data_source_obj_id, connection);
5020  }
5021  }
5022 
5023  @Deprecated
5024  private BlackboardArtifact newBlackboardArtifact(int artifact_type_id, long obj_id, String artifactTypeName, String artifactDisplayName) throws TskCoreException {
5025  try (CaseDbConnection connection = connections.getConnection()) {
5026  long data_source_obj_id = getDataSourceObjectId(connection, obj_id);
5027  return this.newBlackboardArtifact(artifact_type_id, obj_id, artifactTypeName, artifactDisplayName, data_source_obj_id, connection);
5028  }
5029  }
5030 
5031  PreparedStatement createInsertArtifactStatement(int artifact_type_id, long obj_id, long artifact_obj_id, long data_source_obj_id, CaseDbConnection connection) throws TskCoreException, SQLException {
5032 
5033  PreparedStatement statement;
5034  if (dbType == DbType.POSTGRESQL) {
5035  statement = connection.getPreparedStatement(PREPARED_STATEMENT.POSTGRESQL_INSERT_ARTIFACT, Statement.RETURN_GENERATED_KEYS);
5036  statement.clearParameters();
5037  statement.setLong(1, obj_id);
5038  statement.setLong(2, artifact_obj_id);
5039  statement.setLong(3, data_source_obj_id);
5040  statement.setInt(4, artifact_type_id);
5041  } else {
5042  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_ARTIFACT, Statement.RETURN_GENERATED_KEYS);
5043  statement.clearParameters();
5044  this.nextArtifactId++;
5045  statement.setLong(1, this.nextArtifactId);
5046  statement.setLong(2, obj_id);
5047  statement.setLong(3, artifact_obj_id);
5048  statement.setLong(4, data_source_obj_id);
5049  statement.setInt(5, artifact_type_id);
5050  }
5051 
5052  return statement;
5053  }
5054 
5071  @Deprecated
5072  private BlackboardArtifact newBlackboardArtifact(int artifact_type_id, long obj_id, String artifactTypeName, String artifactDisplayName, long data_source_obj_id, CaseDbConnection connection) throws TskCoreException {
5073  BlackboardArtifact.Type type = blackboard.getArtifactType(artifact_type_id);
5074  try {
5075  if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) {
5076  return blackboard.newAnalysisResult(type, obj_id, data_source_obj_id, Score.SCORE_UNKNOWN, null, null, null, Collections.emptyList()).getAnalysisResult();
5077  } else {
5078  return blackboard.newDataArtifact(type, obj_id, data_source_obj_id, Collections.emptyList(), null);
5079  }
5080  } catch (BlackboardException ex) {
5081  throw new TskCoreException("Error creating a blackboard artifact", ex);
5082  }
5083  }
5084 
5103  AnalysisResult newAnalysisResult(BlackboardArtifact.Type artifactType, long objId, Long dataSourceObjId, Score score, String conclusion, String configuration, String justification, CaseDbConnection connection) throws TskCoreException {
5104 
5105  if (artifactType.getCategory() != BlackboardArtifact.Category.ANALYSIS_RESULT) {
5106  throw new TskCoreException(String.format("Artifact type (name = %s) is not of the AnalysisResult category. ", artifactType.getTypeName()));
5107  }
5108 
5109  long artifactID;
5111  try {
5112  // add a row in tsk_objects
5113  long artifactObjId = addObject(objId, TskData.ObjectType.ARTIFACT.getObjectType(), connection);
5114 
5115  // add a row in blackboard_artifacts table
5116  PreparedStatement insertArtifactstatement;
5117  ResultSet resultSet = null;
5118  try {
5119  insertArtifactstatement = createInsertArtifactStatement(artifactType.getTypeID(), objId, artifactObjId, dataSourceObjId, connection);
5120  connection.executeUpdate(insertArtifactstatement);
5121  resultSet = insertArtifactstatement.getGeneratedKeys();
5122  resultSet.next();
5123  artifactID = resultSet.getLong(1); //last_insert_rowid()
5124 
5125  // add a row in tsk_analysis_results if any data for it is set
5126  if (score.getSignificance() != Score.Significance.UNKNOWN
5127  || !StringUtils.isBlank(conclusion)
5128  || !StringUtils.isBlank(configuration)
5129  || !StringUtils.isBlank(justification)) {
5130 
5131  PreparedStatement analysisResultsStatement;
5132 
5133  analysisResultsStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_ANALYSIS_RESULT);
5134  analysisResultsStatement.clearParameters();
5135 
5136  analysisResultsStatement.setLong(1, artifactObjId);
5137  analysisResultsStatement.setString(2, (conclusion != null) ? conclusion : "");
5138  analysisResultsStatement.setInt(3, score.getSignificance().getId());
5139  analysisResultsStatement.setInt(4, score.getPriority().getId());
5140  analysisResultsStatement.setString(5, (configuration != null) ? configuration : "");
5141  analysisResultsStatement.setString(6, (justification != null) ? justification : "");
5142 
5143  connection.executeUpdate(analysisResultsStatement);
5144  }
5145 
5146  return new AnalysisResult(this, artifactID, objId, artifactObjId, dataSourceObjId, artifactType.getTypeID(),
5147  artifactType.getTypeName(), artifactType.getDisplayName(),
5148  BlackboardArtifact.ReviewStatus.UNDECIDED, true,
5149  score, (conclusion != null) ? conclusion : "",
5150  (configuration != null) ? configuration : "", (justification != null) ? justification : "");
5151 
5152  } finally {
5153  closeResultSet(resultSet);
5154  }
5155 
5156  } catch (SQLException ex) {
5157  throw new TskCoreException("Error creating a analysis result", ex);
5158  } finally {
5160  }
5161  }
5162 
5175  boolean getContentHasChildren(Content content) throws TskCoreException {
5176  CaseDbConnection connection = null;
5177  ResultSet rs = null;
5179  try {
5180  connection = connections.getConnection();
5181 
5182  // SELECT COUNT(obj_id) AS count FROM tsk_objects WHERE par_obj_id = ?
5183  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_CHILD_OBJECTS_BY_PARENT);
5184  statement.clearParameters();
5185  statement.setLong(1, content.getId());
5186  rs = connection.executeQuery(statement);
5187  boolean hasChildren = false;
5188  if (rs.next()) {
5189  hasChildren = rs.getInt("count") > 0;
5190  }
5191  return hasChildren;
5192  } catch (SQLException e) {
5193  throw new TskCoreException("Error checking for children of parent " + content, e);
5194  } finally {
5195  closeResultSet(rs);
5196  closeConnection(connection);
5198  }
5199  }
5200 
5213  int getContentChildrenCount(Content content) throws TskCoreException {
5214 
5215  if (!this.getHasChildren(content)) {
5216  return 0;
5217  }
5218 
5219  CaseDbConnection connection = null;
5220  ResultSet rs = null;
5222  try {
5223  connection = connections.getConnection();
5224 
5225  // SELECT COUNT(obj_id) AS count FROM tsk_objects WHERE par_obj_id = ?
5226  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_CHILD_OBJECTS_BY_PARENT);
5227  statement.clearParameters();
5228  statement.setLong(1, content.getId());
5229  rs = connection.executeQuery(statement);
5230  int countChildren = -1;
5231  if (rs.next()) {
5232  countChildren = rs.getInt("count");
5233  }
5234  return countChildren;
5235  } catch (SQLException e) {
5236  throw new TskCoreException("Error checking for children of parent " + content, e);
5237  } finally {
5238  closeResultSet(rs);
5239  closeConnection(connection);
5241  }
5242  }
5243 
5255  List<Content> getAbstractFileChildren(Content parent, TSK_DB_FILES_TYPE_ENUM type) throws TskCoreException {
5256  CaseDbConnection connection = null;
5257  ResultSet rs = null;
5259  try {
5260  connection = connections.getConnection();
5261 
5262  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_PARENT_AND_TYPE);
5263  statement.clearParameters();
5264  long parentId = parent.getId();
5265  statement.setLong(1, parentId);
5266  statement.setShort(2, type.getFileType());
5267  rs = connection.executeQuery(statement);
5268  return fileChildren(rs, connection, parentId);
5269  } catch (SQLException ex) {
5270  throw new TskCoreException("Error getting AbstractFile children for Content", ex);
5271  } finally {
5272  closeResultSet(rs);
5273  closeConnection(connection);
5275  }
5276  }
5277 
5287  List<Content> getAbstractFileChildren(Content parent) throws TskCoreException {
5288  CaseDbConnection connection = null;
5289  ResultSet rs = null;
5291  try {
5292  connection = connections.getConnection();
5293 
5294  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_PARENT);
5295  statement.clearParameters();
5296  long parentId = parent.getId();
5297  statement.setLong(1, parentId);
5298  rs = connection.executeQuery(statement);
5299  return fileChildren(rs, connection, parentId);
5300  } catch (SQLException ex) {
5301  throw new TskCoreException("Error getting AbstractFile children for Content", ex);
5302  } finally {
5303  closeResultSet(rs);
5304  closeConnection(connection);
5306  }
5307  }
5308 
5320  List<Long> getAbstractFileChildrenIds(Content parent, TSK_DB_FILES_TYPE_ENUM type) throws TskCoreException {
5321  CaseDbConnection connection = null;
5322  ResultSet rs = null;
5324  try {
5325  connection = connections.getConnection();
5326 
5327  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_IDS_BY_PARENT_AND_TYPE);
5328  statement.clearParameters();
5329  statement.setLong(1, parent.getId());
5330  statement.setShort(2, type.getFileType());
5331  rs = connection.executeQuery(statement);
5332  List<Long> children = new ArrayList<Long>();
5333  while (rs.next()) {
5334  children.add(rs.getLong("obj_id"));
5335  }
5336  return children;
5337  } catch (SQLException ex) {
5338  throw new TskCoreException("Error getting AbstractFile children for Content", ex);
5339  } finally {
5340  closeResultSet(rs);
5341  closeConnection(connection);
5343  }
5344  }
5345 
5355  List<Long> getAbstractFileChildrenIds(Content parent) throws TskCoreException {
5356  CaseDbConnection connection = null;
5357  ResultSet rs = null;
5359  try {
5360  connection = connections.getConnection();
5361 
5362  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_IDS_BY_PARENT);
5363  statement.clearParameters();
5364  statement.setLong(1, parent.getId());
5365  rs = connection.executeQuery(statement);
5366  List<Long> children = new ArrayList<Long>();
5367  while (rs.next()) {
5368  children.add(rs.getLong("obj_id"));
5369  }
5370  return children;
5371  } catch (SQLException ex) {
5372  throw new TskCoreException("Error getting AbstractFile children for Content", ex);
5373  } finally {
5374  closeResultSet(rs);
5375  closeConnection(connection);
5377  }
5378  }
5379 
5390  List<Long> getBlackboardArtifactChildrenIds(Content parent) throws TskCoreException {
5391  CaseDbConnection connection = null;
5392  ResultSet rs = null;
5394  try {
5395  connection = connections.getConnection();
5396 
5397  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ARTIFACT_OBJECTIDS_BY_PARENT);
5398  statement.clearParameters();
5399  statement.setLong(1, parent.getId());
5400  rs = connection.executeQuery(statement);
5401  List<Long> children = new ArrayList<Long>();
5402  while (rs.next()) {
5403  children.add(rs.getLong("obj_id"));
5404  }
5405  return children;
5406  } catch (SQLException ex) {
5407  throw new TskCoreException("Error getting children for BlackboardArtifact", ex);
5408  } finally {
5409  closeResultSet(rs);
5410  closeConnection(connection);
5412  }
5413  }
5414 
5424  List<Content> getBlackboardArtifactChildren(Content parent) throws TskCoreException {
5425  long parentId = parent.getId();
5426  List<Content> lc = new ArrayList<>();
5427  lc.addAll(blackboard.getAnalysisResults(parentId));
5428  lc.addAll(blackboard.getDataArtifactsBySource(parentId));
5429  return lc;
5430  }
5431 
5440  Collection<ObjectInfo> getChildrenInfo(Content c) throws TskCoreException {
5441  CaseDbConnection connection = null;
5442  Statement s = null;
5443  ResultSet rs = null;
5445  try {
5446  connection = connections.getConnection();
5447  s = connection.createStatement();
5448  rs = connection.executeQuery(s, "SELECT tsk_objects.obj_id AS obj_id, tsk_objects.type AS type " //NON-NLS
5449  + "FROM tsk_objects LEFT JOIN tsk_files " //NON-NLS
5450  + "ON tsk_objects.obj_id = tsk_files.obj_id " //NON-NLS
5451  + "WHERE tsk_objects.par_obj_id = " + c.getId()
5452  + " ORDER BY tsk_objects.obj_id"); //NON-NLS
5453  Collection<ObjectInfo> infos = new ArrayList<ObjectInfo>();
5454  while (rs.next()) {
5455  infos.add(new ObjectInfo(rs.getLong("obj_id"), ObjectType.valueOf(rs.getShort("type")))); //NON-NLS
5456  }
5457  return infos;
5458  } catch (SQLException ex) {
5459  throw new TskCoreException("Error getting Children Info for Content", ex);
5460  } finally {
5461  closeResultSet(rs);
5462  closeStatement(s);
5463  closeConnection(connection);
5465  }
5466  }
5467 
5478  ObjectInfo getParentInfo(Content c) throws TskCoreException {
5479  return getParentInfo(c.getId());
5480  }
5481 
5492  ObjectInfo getParentInfo(long contentId) throws TskCoreException {
5494  CaseDbConnection connection = null;
5495  Statement s = null;
5496  ResultSet rs = null;
5497  try {
5498  connection = connections.getConnection();
5499  s = connection.createStatement();
5500  rs = connection.executeQuery(s, "SELECT parent.obj_id AS obj_id, parent.type AS type " //NON-NLS
5501  + "FROM tsk_objects AS parent INNER JOIN tsk_objects AS child " //NON-NLS
5502  + "ON child.par_obj_id = parent.obj_id " //NON-NLS
5503  + "WHERE child.obj_id = " + contentId); //NON-NLS
5504  if (rs.next()) {
5505  return new ObjectInfo(rs.getLong("obj_id"), ObjectType.valueOf(rs.getShort("type")));
5506  } else {
5507  return null;
5508  }
5509  } catch (SQLException ex) {
5510  throw new TskCoreException("Error getting Parent Info for Content: " + contentId, ex);
5511  } finally {
5512  closeResultSet(rs);
5513  closeStatement(s);
5514  closeConnection(connection);
5516  }
5517  }
5518 
5529  Directory getParentDirectory(FsContent fsc) throws TskCoreException {
5530  if (fsc.isRoot()) {
5531  // Given FsContent is a root object and can't have parent directory
5532  return null;
5533  } else {
5534  ObjectInfo parentInfo = getParentInfo(fsc);
5535  if (parentInfo == null) {
5536  return null;
5537  }
5538  Directory parent = null;
5539  if (parentInfo.type == ObjectType.ABSTRACTFILE) {
5540  parent = getDirectoryById(parentInfo.id, fsc.getFileSystem());
5541  } else {
5542  throw new TskCoreException("Parent of FsContent (id: " + fsc.getId() + ") has wrong type to be directory: " + parentInfo.type);
5543  }
5544  return parent;
5545  }
5546  }
5547 
5559  public Content getContentById(long id) throws TskCoreException {
5560  // First check to see if this exists in our frequently used content cache.
5561  Content content = frequentlyUsedContentMap.get(id);
5562  if (null != content) {
5563  return content;
5564  }
5565 
5566  long parentId;
5567  TskData.ObjectType type;
5568 
5569  CaseDbConnection connection = null;
5570  Statement s = null;
5571  ResultSet rs = null;
5573  try {
5574  connection = connections.getConnection();
5575  s = connection.createStatement();
5576  rs = connection.executeQuery(s, "SELECT * FROM tsk_objects WHERE obj_id = " + id + " LIMIT 1"); //NON-NLS
5577  if (!rs.next()) {
5578  return null;
5579  }
5580  parentId = rs.getLong("par_obj_id"); //NON-NLS
5581  type = TskData.ObjectType.valueOf(rs.getShort("type")); //NON-NLS
5582  } catch (SQLException ex) {
5583  throw new TskCoreException("Error getting Content by ID.", ex);
5584  } finally {
5585  closeResultSet(rs);
5586  closeStatement(s);
5587  closeConnection(connection);
5589  }
5590 
5591  // Construct the object
5592  switch (type) {
5593  case IMG:
5594  content = getImageById(id);
5595  frequentlyUsedContentMap.put(id, content);
5596  break;
5597  case VS:
5598  content = getVolumeSystemById(id, parentId);
5599  break;
5600  case VOL:
5601  content = getVolumeById(id, parentId);
5602  frequentlyUsedContentMap.put(id, content);
5603  break;
5604  case POOL:
5605  content = getPoolById(id, parentId);
5606  break;
5607  case FS:
5608  content = getFileSystemById(id, parentId);
5609  frequentlyUsedContentMap.put(id, content);
5610  break;
5611  case ABSTRACTFILE:
5612  content = getAbstractFileById(id);
5613 
5614  // Add virtual and root directories to frequently used map.
5615  // Calling isRoot() on local directories goes up the entire directory structure
5616  // and they can only be the root of portable cases, so skip trying to add
5617  // them to the cache.
5618  if (((AbstractFile) content).isVirtual()
5619  || ((!(content instanceof LocalDirectory)) && ((AbstractFile) content).isRoot())) {
5620  frequentlyUsedContentMap.put(id, content);
5621  }
5622  break;
5623  case ARTIFACT:
5624  content = getArtifactById(id);
5625  break;
5626  case REPORT:
5627  content = getReportById(id);
5628  break;
5629  case OS_ACCOUNT:
5630  content = this.osAccountManager.getOsAccountByObjectId(id);
5631  break;
5632  case HOST_ADDRESS:
5633  content = hostAddressManager.getHostAddress(id);
5634  break;
5635  default:
5636  content = new UnsupportedContent(this, id);
5637  }
5638 
5639  return content;
5640  }
5641 
5649  String getFilePath(long id) {
5650 
5651  String filePath = null;
5652  CaseDbConnection connection = null;
5653  ResultSet rs = null;
5655  try {
5656  connection = connections.getConnection();
5657 
5658  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_LOCAL_PATH_FOR_FILE);
5659  statement.clearParameters();
5660  statement.setLong(1, id);
5661  rs = connection.executeQuery(statement);
5662  if (rs.next()) {
5663  filePath = rs.getString("path");
5664  }
5665  } catch (SQLException | TskCoreException ex) {
5666  logger.log(Level.SEVERE, "Error getting file path for file " + id, ex); //NON-NLS
5667  } finally {
5668  closeResultSet(rs);
5669  closeConnection(connection);
5671  }
5672  return filePath;
5673  }
5674 
5682  TskData.EncodingType getEncodingType(long id) {
5683 
5684  TskData.EncodingType type = TskData.EncodingType.NONE;
5685  CaseDbConnection connection = null;
5686  ResultSet rs = null;
5688  try {
5689  connection = connections.getConnection();
5690  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ENCODING_FOR_FILE);
5691  statement.clearParameters();
5692  statement.setLong(1, id);
5693  rs = connection.executeQuery(statement);
5694  if (rs.next()) {
5695  type = TskData.EncodingType.valueOf(rs.getInt(1));
5696  }
5697  } catch (SQLException | TskCoreException ex) {
5698  logger.log(Level.SEVERE, "Error getting encoding type for file " + id, ex); //NON-NLS
5699  } finally {
5700  closeResultSet(rs);
5701  closeConnection(connection);
5703  }
5704  return type;
5705  }
5706 
5715  String getFileParentPath(long objectId, CaseDbConnection connection) {
5716  String parentPath = null;
5718  ResultSet rs = null;
5719  try {
5720  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_PATH_FOR_FILE);
5721  statement.clearParameters();
5722  statement.setLong(1, objectId);
5723  rs = connection.executeQuery(statement);
5724  if (rs.next()) {
5725  parentPath = rs.getString("parent_path");
5726  }
5727  } catch (SQLException ex) {
5728  logger.log(Level.SEVERE, "Error getting file parent_path for file " + objectId, ex); //NON-NLS
5729  } finally {
5730  closeResultSet(rs);
5732  }
5733  return parentPath;
5734  }
5735 
5744  String getFileName(long objectId, CaseDbConnection connection) {
5745  String fileName = null;
5747  ResultSet rs = null;
5748  try {
5749  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_NAME);
5750  statement.clearParameters();
5751  statement.setLong(1, objectId);
5752  rs = connection.executeQuery(statement);
5753  if (rs.next()) {
5754  fileName = rs.getString("name");
5755  }
5756  } catch (SQLException ex) {
5757  logger.log(Level.SEVERE, "Error getting file parent_path for file " + objectId, ex); //NON-NLS
5758  } finally {
5759  closeResultSet(rs);
5761  }
5762  return fileName;
5763  }
5764 
5775  DerivedFile.DerivedMethod getDerivedMethod(long id) throws TskCoreException {
5776 
5777  DerivedFile.DerivedMethod method = null;
5778  CaseDbConnection connection = null;
5779  ResultSet rs1 = null;
5780  ResultSet rs2 = null;
5782  try {
5783  connection = connections.getConnection();
5784 
5785  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_DERIVED_FILE);
5786  statement.clearParameters();
5787  statement.setLong(1, id);
5788  rs1 = connection.executeQuery(statement);
5789  if (rs1.next()) {
5790  int method_id = rs1.getInt("derived_id");
5791  String rederive = rs1.getString("rederive");
5792  method = new DerivedFile.DerivedMethod(method_id, rederive);
5793  statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_DERIVATION_METHOD);
5794  statement.clearParameters();
5795  statement.setInt(1, method_id);
5796  rs2 = connection.executeQuery(statement);
5797  if (rs2.next()) {
5798  method.setToolName(rs2.getString("tool_name"));
5799  method.setToolVersion(rs2.getString("tool_version"));
5800  method.setOther(rs2.getString("other"));
5801  }
5802  }
5803  } catch (SQLException e) {
5804  logger.log(Level.SEVERE, "Error getting derived method for file: " + id, e); //NON-NLS
5805  } finally {
5806  closeResultSet(rs2);
5807  closeResultSet(rs1);
5808  closeConnection(connection);
5810  }
5811  return method;
5812  }
5813 
5824  public AbstractFile getAbstractFileById(long id) throws TskCoreException {
5825  CaseDbConnection connection = connections.getConnection();
5826  try {
5827  return getAbstractFileById(id, connection);
5828  } finally {
5829  closeConnection(connection);
5830  }
5831  }
5832 
5845  AbstractFile getAbstractFileById(long objectId, CaseDbConnection connection) throws TskCoreException {
5847  ResultSet rs = null;
5848  try {
5849  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_BY_ID);
5850  statement.clearParameters();
5851  statement.setLong(1, objectId);
5852  rs = connection.executeQuery(statement);
5853  List<AbstractFile> files = resultSetToAbstractFiles(rs, connection);
5854  if (files.size() > 0) {
5855  return files.get(0);
5856  } else {
5857  return null;
5858  }
5859  } catch (SQLException ex) {
5860  throw new TskCoreException("Error getting file by id, id = " + objectId, ex);
5861  } finally {
5862  closeResultSet(rs);
5864  }
5865  }
5866 
5878  public BlackboardArtifact getArtifactById(long id) throws TskCoreException {
5879 
5880  CaseDbConnection connection = null;
5881  ResultSet rs = null;
5883  try {
5884  connection = connections.getConnection();
5885 
5886  // get the artifact type.
5887  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ARTIFACT_TYPE_BY_ARTIFACT_OBJ_ID);
5888  statement.clearParameters();
5889  statement.setLong(1, id);
5890 
5891  rs = connection.executeQuery(statement);
5892  if (!rs.next()) {
5893  throw new TskCoreException("Error getting artifacttype for artifact with artifact_obj_id = " + id);
5894  }
5895 
5896  // based on the artifact type category, get the analysis result or the data artifact
5897  BlackboardArtifact.Type artifactType = blackboard.getArtifactType(rs.getInt("artifact_type_id"));
5898  switch (artifactType.getCategory()) {
5899  case ANALYSIS_RESULT:
5900  return blackboard.getAnalysisResultById(id);
5901  case DATA_ARTIFACT:
5902  return blackboard.getDataArtifactById(id);
5903  default:
5904  throw new TskCoreException(String.format("Unknown artifact category for artifact with artifact_obj_id = %d, and artifact type = %s", id, artifactType.getTypeName()));
5905  }
5906 
5907  } catch (SQLException ex) {
5908  throw new TskCoreException("Error getting artifacts by artifact_obj_id, artifact_obj_id = " + id, ex);
5909  } finally {
5910  closeResultSet(rs);
5911  closeConnection(connection);
5913  }
5914  }
5915 
5929  @Deprecated
5930  public BlackboardArtifact getArtifactByArtifactId(long id) throws TskCoreException {
5931  String query = "SELECT artifact_type_id, artifact_obj_id FROM blackboard_artifacts WHERE artifact_id = " + id;
5933 
5934  try (CaseDbConnection connection = connections.getConnection();
5935  Statement statement = connection.createStatement();
5936  ResultSet resultSet = statement.executeQuery(query);) {
5937  if (resultSet != null && resultSet.next()) {
5938  BlackboardArtifact.Type artifactType = blackboard.getArtifactType(resultSet.getInt("artifact_type_id"));
5939  long artifactObjId = resultSet.getLong("artifact_obj_id");
5940  switch (artifactType.getCategory()) {
5941  case ANALYSIS_RESULT:
5942  return blackboard.getAnalysisResultById(artifactObjId);
5943  case DATA_ARTIFACT:
5944  return blackboard.getDataArtifactById(artifactObjId);
5945  }
5946  }
5947  return null;
5948  } catch (SQLException ex) {
5949  throw new TskCoreException("Error getting artifacts by artifact id, artifact id = " + id, ex);
5950  } finally {
5952  }
5953  }
5954 
5967  private long getFileSystemId(long fileId, CaseDbConnection connection) {
5969  ResultSet rs = null;
5970  long ret = -1;
5971  try {
5972  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_SYSTEM_BY_OBJECT);
5973  statement.clearParameters();
5974  statement.setLong(1, fileId);
5975  rs = connection.executeQuery(statement);
5976  if (rs.next()) {
5977  ret = rs.getLong("fs_obj_id");
5978  if (ret == 0) {
5979  ret = -1;
5980  }
5981  }
5982  } catch (SQLException e) {
5983  logger.log(Level.SEVERE, "Error checking file system id of a file, id = " + fileId, e); //NON-NLS
5984  } finally {
5985  closeResultSet(rs);
5987  }
5988  return ret;
5989  }
5990 
6002  public boolean isFileFromSource(Content dataSource, long fileId) throws TskCoreException {
6003  String query = String.format("SELECT COUNT(*) AS count FROM tsk_files WHERE obj_id = %d AND data_source_obj_id = %d", fileId, dataSource.getId()); //NON-NLS
6004  CaseDbConnection connection = null;
6005  Statement statement = null;
6006  ResultSet resultSet = null;
6008  try {
6009  connection = connections.getConnection();
6010  statement = connection.createStatement();
6011  resultSet = connection.executeQuery(statement, query);
6012  resultSet.next();
6013  return (resultSet.getLong("count") > 0L);
6014  } catch (SQLException ex) {
6015  throw new TskCoreException(String.format("Error executing query %s", query), ex);
6016  } finally {
6017  closeResultSet(resultSet);
6018  closeStatement(statement);
6019  closeConnection(connection);
6021  }
6022  }
6023 
6033  private static boolean containsLikeWildcard(String str) {
6034  if (str == null) {
6035  return false;
6036  } else {
6037  return str.contains("%") || str.contains("_");
6038  }
6039  }
6040 
6052  public List<AbstractFile> findFiles(Content dataSource, String fileName) throws TskCoreException {
6053  String ext = "";
6054  if (!containsLikeWildcard(fileName)) {
6055  ext = SleuthkitCase.extractExtension(fileName);
6056  }
6057 
6058  List<AbstractFile> files = new ArrayList<>();
6059  CaseDbConnection connection = null;
6060  ResultSet resultSet = null;
6062  try {
6063  connection = connections.getConnection();
6064 
6065  PreparedStatement statement;
6066  if (ext.isEmpty()) {
6067  statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_DATA_SOURCE_AND_NAME);
6068  statement.clearParameters();
6069  statement.setString(1, fileName.toLowerCase());
6070  statement.setLong(2, dataSource.getId());
6071  } else {
6072  statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_EXTENSION_AND_DATA_SOURCE_AND_NAME);
6073  statement.clearParameters();
6074  statement.setString(1, ext);
6075  statement.setString(2, fileName.toLowerCase());
6076  statement.setLong(3, dataSource.getId());
6077  }
6078 
6079  resultSet = connection.executeQuery(statement);
6080  files.addAll(resultSetToAbstractFiles(resultSet, connection));
6081  } catch (SQLException e) {
6082  throw new TskCoreException(bundle.getString("SleuthkitCase.findFiles.exception.msg3.text"), e);
6083  } finally {
6084  closeResultSet(resultSet);
6085  closeConnection(connection);
6087  }
6088  return files;
6089  }
6090 
6104  public List<AbstractFile> findFiles(Content dataSource, String fileName, String dirSubString) throws TskCoreException {
6105  String ext = "";
6106  if (!containsLikeWildcard(fileName)) {
6107  ext = SleuthkitCase.extractExtension(fileName);
6108  }
6109 
6110  List<AbstractFile> files = new ArrayList<>();
6111  CaseDbConnection connection = null;
6112  ResultSet resultSet = null;
6114  try {
6115  connection = connections.getConnection();
6116  PreparedStatement statement;
6117  if (ext.isEmpty()) {
6118  statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_DATA_SOURCE_AND_PARENT_PATH_AND_NAME);
6119  statement.clearParameters();
6120  statement.setString(1, fileName.toLowerCase());
6121  statement.setString(2, "%" + dirSubString.toLowerCase() + "%"); //NON-NLS
6122  statement.setLong(3, dataSource.getId());
6123  } else {
6124  statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_EXTENSION_AND_DATA_SOURCE_AND_PARENT_PATH_AND_NAME);
6125  statement.clearParameters();
6126  statement.setString(1, ext);
6127  statement.setString(2, fileName.toLowerCase());
6128  statement.setString(3, "%" + dirSubString.toLowerCase() + "%"); //NON-NLS
6129  statement.setLong(4, dataSource.getId());
6130  }
6131 
6132  resultSet = connection.executeQuery(statement);
6133  files.addAll(resultSetToAbstractFiles(resultSet, connection));
6134  } catch (SQLException e) {
6135  throw new TskCoreException(bundle.getString("SleuthkitCase.findFiles3.exception.msg3.text"), e);
6136  } finally {
6137  closeResultSet(resultSet);
6138  closeConnection(connection);
6140  }
6141  return files;
6142  }
6143 
6155  public VirtualDirectory addVirtualDirectory(long parentId, String directoryName) throws TskCoreException {
6156  CaseDbTransaction localTrans = beginTransaction();
6157  try {
6158  VirtualDirectory newVD = addVirtualDirectory(parentId, directoryName, localTrans);
6159  localTrans.commit();
6160  localTrans = null;
6161  return newVD;
6162  } finally {
6163  if (null != localTrans) {
6164  try {
6165  localTrans.rollback();
6166  } catch (TskCoreException ex2) {
6167  logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex2);
6168  }
6169  }
6170  }
6171  }
6172 
6185  long addObject(long parentId, int objectType, CaseDbConnection connection) throws SQLException {
6186  ResultSet resultSet = null;
6188  try {
6189  // INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)
6190  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_OBJECT, Statement.RETURN_GENERATED_KEYS);
6191  statement.clearParameters();
6192  if (parentId != 0) {
6193  statement.setLong(1, parentId);
6194  } else {
6195  statement.setNull(1, java.sql.Types.BIGINT);
6196  }
6197  statement.setInt(2, objectType);
6198  connection.executeUpdate(statement);
6199  resultSet = statement.getGeneratedKeys();
6200 
6201  if (resultSet.next()) {
6202  if (parentId != 0) {
6203  setHasChildren(parentId);
6204  }
6205  return resultSet.getLong(1); //last_insert_rowid()
6206  } else {
6207  throw new SQLException("Error inserting object with parent " + parentId + " into tsk_objects");
6208  }
6209  } finally {
6210  closeResultSet(resultSet);
6212  }
6213  }
6214 
6232  public VirtualDirectory addVirtualDirectory(long parentId, String directoryName, CaseDbTransaction transaction) throws TskCoreException {
6233  if (transaction == null) {
6234  throw new TskCoreException("Passed null CaseDbTransaction");
6235  }
6236 
6237  ResultSet resultSet = null;
6238  try {
6239  // Get the parent path.
6240  CaseDbConnection connection = transaction.getConnection();
6241 
6242  String parentPath;
6243  Content parent = this.getAbstractFileById(parentId, connection);
6244  if (parent instanceof AbstractFile) {
6245  if (isRootDirectory((AbstractFile) parent, transaction)) {
6246  if (parent.getName().isEmpty()) {
6247  parentPath = "/";
6248  } else {
6249  parentPath = "/" + parent.getName() + "/";
6250  }
6251  } else {
6252  parentPath = ((AbstractFile) parent).getParentPath() + parent.getName() + "/"; //NON-NLS
6253  }
6254  } else {
6255  // The parent was either null or not an abstract file
6256  parentPath = "/";
6257  }
6258 
6259  // Insert a row for the virtual directory into the tsk_objects table.
6260  long newObjId = addObject(parentId, TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection);
6261 
6262  // Insert a row for the virtual directory into the tsk_files table.
6263  // INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type,
6264  // dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, sha256, sha1, known, mime_type, parent_path, data_source_obj_id,extension,owner_uid, os_account_obj_id)
6265  // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?,?,?,?)
6266  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
6267  statement.clearParameters();
6268  statement.setLong(1, newObjId);
6269 
6270  // If the parent is part of a file system, grab its file system ID
6271  Long fileSystemObjectId = null;
6272  if (0 != parentId) {
6273  fileSystemObjectId = this.getFileSystemId(parentId, connection);
6274  if (fileSystemObjectId != -1) {
6275  statement.setLong(2, fileSystemObjectId);
6276  } else {
6277  statement.setNull(2, java.sql.Types.BIGINT);
6278  fileSystemObjectId = null;
6279  }
6280  } else {
6281  statement.setNull(2, java.sql.Types.BIGINT);
6282  }
6283 
6284  // name
6285  statement.setString(3, directoryName);
6286 
6287  //type
6288  statement.setShort(4, TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType());
6289  statement.setShort(5, (short) 1);
6290 
6291  //flags
6293  statement.setShort(6, dirType.getValue());
6295  statement.setShort(7, metaType.getValue());
6296 
6297  //allocated
6299  statement.setShort(8, dirFlag.getValue());
6300  final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue()
6301  | TSK_FS_META_FLAG_ENUM.USED.getValue());
6302  statement.setShort(9, metaFlags);
6303 
6304  //size
6305  statement.setLong(10, 0);
6306 
6307  // nulls for params 11-14
6308  statement.setNull(11, java.sql.Types.BIGINT);
6309  statement.setNull(12, java.sql.Types.BIGINT);
6310  statement.setNull(13, java.sql.Types.BIGINT);
6311  statement.setNull(14, java.sql.Types.BIGINT);
6312 
6313  statement.setNull(15, java.sql.Types.VARCHAR); // MD5
6314  statement.setNull(16, java.sql.Types.VARCHAR); // SHA-256
6315  statement.setNull(17, java.sql.Types.VARCHAR); // SHA-1
6316 
6317  statement.setByte(18, FileKnown.UNKNOWN.getFileKnownValue()); // Known
6318  statement.setNull(19, java.sql.Types.VARCHAR); // MIME type
6319 
6320  // parent path
6321  statement.setString(20, parentPath);
6322 
6323  // data source object id (same as object id if this is a data source)
6324  long dataSourceObjectId;
6325  if (0 == parentId) {
6326  dataSourceObjectId = newObjId;
6327  } else {
6328  dataSourceObjectId = getDataSourceObjectId(connection, parentId);
6329  }
6330  statement.setLong(21, dataSourceObjectId);
6331 
6332  //extension, since this is not really file we just set it to null
6333  statement.setString(22, null);
6334 
6335  statement.setString(23, OsAccount.NO_OWNER_ID); // ownerUid
6336  statement.setNull(24, java.sql.Types.BIGINT); // osAccountObjId
6337 
6338  connection.executeUpdate(statement);
6339 
6340  return new VirtualDirectory(this, newObjId, dataSourceObjectId, fileSystemObjectId, directoryName, dirType,
6341  metaType, dirFlag, metaFlags, null, null, null, FileKnown.UNKNOWN,
6342  parentPath);
6343  } catch (SQLException e) {
6344  throw new TskCoreException("Error creating virtual directory '" + directoryName + "'", e);
6345  } finally {
6346  closeResultSet(resultSet);
6347  }
6348  }
6349 
6362  public LocalDirectory addLocalDirectory(long parentId, String directoryName) throws TskCoreException {
6363  CaseDbTransaction localTrans = beginTransaction();
6364  try {
6365  LocalDirectory newLD = addLocalDirectory(parentId, directoryName, localTrans);
6366  localTrans.commit();
6367  return newLD;
6368  } catch (TskCoreException ex) {
6369  try {
6370  localTrans.rollback();
6371  } catch (TskCoreException ex2) {
6372  logger.log(Level.SEVERE, String.format("Failed to rollback transaction after exception: %s", ex.getMessage()), ex2);
6373  }
6374  throw ex;
6375  }
6376  }
6377 
6395  public LocalDirectory addLocalDirectory(long parentId, String directoryName, CaseDbTransaction transaction) throws TskCoreException {
6396  if (transaction == null) {
6397  throw new TskCoreException("Passed null CaseDbTransaction");
6398  }
6399 
6400  ResultSet resultSet = null;
6401  try {
6402  // Get the parent path.
6403  CaseDbConnection connection = transaction.getConnection();
6404  AbstractFile parent = getAbstractFileById(parentId, connection);
6405  String parentPath;
6406  if ((parent == null) || isRootDirectory(parent, transaction)) {
6407  parentPath = "/";
6408  } else {
6409  parentPath = parent.getParentPath() + parent.getName() + "/"; //NON-NLS
6410  }
6411 
6412  // Insert a row for the local directory into the tsk_objects table.
6413  long newObjId = addObject(parentId, TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection);
6414 
6415  // Insert a row for the local directory into the tsk_files table.
6416  // INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type,
6417  // dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, sha256, sha1, known, mime_type, parent_path, data_source_obj_id, extension, owner_uid, os_account_obj_id)
6418  // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
6419  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
6420  statement.clearParameters();
6421  statement.setLong(1, newObjId);
6422 
6423  // The parent of a local directory will never be a file system
6424  statement.setNull(2, java.sql.Types.BIGINT);
6425 
6426  // name
6427  statement.setString(3, directoryName);
6428 
6429  //type
6430  statement.setShort(4, TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL_DIR.getFileType());
6431  statement.setShort(5, (short) 1);
6432 
6433  //flags
6435  statement.setShort(6, dirType.getValue());
6437  statement.setShort(7, metaType.getValue());
6438 
6439  //allocated
6441  statement.setShort(8, dirFlag.getValue());
6442  final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue()
6443  | TSK_FS_META_FLAG_ENUM.USED.getValue());
6444  statement.setShort(9, metaFlags);
6445 
6446  //size
6447  statement.setLong(10, 0);
6448 
6449  // nulls for params 11-14
6450  statement.setNull(11, java.sql.Types.BIGINT);
6451  statement.setNull(12, java.sql.Types.BIGINT);
6452  statement.setNull(13, java.sql.Types.BIGINT);
6453  statement.setNull(14, java.sql.Types.BIGINT);
6454 
6455  statement.setNull(15, java.sql.Types.VARCHAR); // MD5
6456  statement.setNull(16, java.sql.Types.VARCHAR); // SHA-256
6457  statement.setNull(17, java.sql.Types.VARCHAR); // SHA-1
6458 
6459  statement.setByte(18, FileKnown.UNKNOWN.getFileKnownValue()); // Known
6460  statement.setNull(19, java.sql.Types.VARCHAR); // MIME type
6461 
6462  // parent path
6463  statement.setString(20, parentPath);
6464 
6465  // data source object id
6466  long dataSourceObjectId = getDataSourceObjectId(connection, parentId);
6467  statement.setLong(21, dataSourceObjectId);
6468 
6469  //extension, since this is a directory we just set it to null
6470  statement.setString(22, null);
6471 
6472  statement.setString(23, OsAccount.NO_OWNER_ID); // ownerUid
6473  statement.setNull(24, java.sql.Types.BIGINT); // osAccountObjId
6474 
6475  connection.executeUpdate(statement);
6476 
6477  return new LocalDirectory(this, newObjId, dataSourceObjectId, directoryName, dirType,
6478  metaType, dirFlag, metaFlags, null, null, null, FileKnown.UNKNOWN,
6479  parentPath);
6480  } catch (SQLException e) {
6481  throw new TskCoreException("Error creating local directory '" + directoryName + "'", e);
6482  } finally {
6483  closeResultSet(resultSet);
6484  }
6485  }
6486 
6506  public LocalFilesDataSource addLocalFilesDataSource(String deviceId, String rootDirectoryName, String timeZone, CaseDbTransaction transaction) throws TskCoreException {
6507  return addLocalFilesDataSource(deviceId, rootDirectoryName, timeZone, null, transaction);
6508  }
6509 
6530  public LocalFilesDataSource addLocalFilesDataSource(String deviceId, String rootDirectoryName, String timeZone, Host host, CaseDbTransaction transaction) throws TskCoreException {
6531 
6532  Statement statement = null;
6533  try {
6534  CaseDbConnection connection = transaction.getConnection();
6535 
6536  // Insert a row for the root virtual directory of the data source
6537  // into the tsk_objects table.
6538  long newObjId = addObject(0, TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection);
6539 
6540  // If no host was supplied, make one
6541  if (host == null) {
6542  host = getHostManager().newHost("LogicalFileSet_" + newObjId + " Host", transaction);
6543  }
6544 
6545  // Insert a row for the virtual directory of the data source into
6546  // the data_source_info table.
6547  statement = connection.createStatement();
6548  statement.executeUpdate("INSERT INTO data_source_info (obj_id, device_id, time_zone, host_id) "
6549  + "VALUES(" + newObjId + ", '" + deviceId + "', '" + timeZone + "', " + host.getHostId() + ");");
6550 
6551  // Insert a row for the root virtual directory of the data source
6552  // into the tsk_files table. Note that its data source object id is
6553  // its own object id.
6554  // INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path,
6555  // dir_type, meta_type, dir_flags, meta_flags, size, ctime, crtime,
6556  // atime, mtime, md5, sha256, sha1, known, mime_type, parent_path, data_source_obj_id, extension, owner_uid, os_account_obj_id)
6557  // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?)
6558  PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
6559  preparedStatement.clearParameters();
6560  preparedStatement.setLong(1, newObjId);
6561  preparedStatement.setNull(2, java.sql.Types.BIGINT);
6562  preparedStatement.setString(3, rootDirectoryName);
6563  preparedStatement.setShort(4, TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType());
6564  preparedStatement.setShort(5, (short) 1);
6566  preparedStatement.setShort(6, TSK_FS_NAME_TYPE_ENUM.DIR.getValue());
6568  preparedStatement.setShort(7, metaType.getValue());
6570  preparedStatement.setShort(8, dirFlag.getValue());
6571  final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue()
6572  | TSK_FS_META_FLAG_ENUM.USED.getValue());
6573  preparedStatement.setShort(9, metaFlags);
6574  preparedStatement.setLong(10, 0);
6575  preparedStatement.setNull(11, java.sql.Types.BIGINT);
6576  preparedStatement.setNull(12, java.sql.Types.BIGINT);
6577  preparedStatement.setNull(13, java.sql.Types.BIGINT);
6578  preparedStatement.setNull(14, java.sql.Types.BIGINT);
6579  preparedStatement.setNull(15, java.sql.Types.VARCHAR); // MD5
6580  preparedStatement.setNull(16, java.sql.Types.VARCHAR); // SHA-256
6581  preparedStatement.setNull(17, java.sql.Types.VARCHAR); // SHA-1
6582  preparedStatement.setByte(18, FileKnown.UNKNOWN.getFileKnownValue()); // Known
6583  preparedStatement.setNull(19, java.sql.Types.VARCHAR); // MIME type
6584  String parentPath = "/"; //NON-NLS
6585  preparedStatement.setString(20, parentPath);
6586  preparedStatement.setLong(21, newObjId);
6587  preparedStatement.setString(22, null); //extension, just set it to null
6588  preparedStatement.setString(23, OsAccount.NO_OWNER_ID); // ownerUid
6589  preparedStatement.setNull(24, java.sql.Types.BIGINT); // osAccountObjId
6590 
6591 
6592  connection.executeUpdate(preparedStatement);
6593 
6594  return new LocalFilesDataSource(this, newObjId, newObjId, deviceId, rootDirectoryName, dirType, metaType, dirFlag, metaFlags, timeZone, null, null, null, FileKnown.UNKNOWN, parentPath);
6595 
6596  } catch (SQLException ex) {
6597  throw new TskCoreException(String.format("Error creating local files data source with device id %s and directory name %s", deviceId, rootDirectoryName), ex);
6598  } finally {
6599  closeStatement(statement);
6600  }
6601  }
6602 
6622  public Image addImage(TskData.TSK_IMG_TYPE_ENUM type, long sectorSize, long size, String displayName, List<String> imagePaths,
6623  String timezone, String md5, String sha1, String sha256,
6624  String deviceId,
6625  CaseDbTransaction transaction) throws TskCoreException {
6626  return addImage(type, sectorSize, size, displayName, imagePaths, timezone, md5, sha1, sha256, deviceId, null, transaction);
6627  }
6628 
6649  public Image addImage(TskData.TSK_IMG_TYPE_ENUM type, long sectorSize, long size, String displayName, List<String> imagePaths,
6650  String timezone, String md5, String sha1, String sha256,
6651  String deviceId, Host host,
6652  CaseDbTransaction transaction) throws TskCoreException {
6653  Statement statement = null;
6654  try {
6655  // Insert a row for the Image into the tsk_objects table.
6656  CaseDbConnection connection = transaction.getConnection();
6657  long newObjId = addObject(0, TskData.ObjectType.IMG.getObjectType(), connection);
6658 
6659  // Add a row to tsk_image_info
6660  // INSERT INTO tsk_image_info (obj_id, type, ssize, tzone, size, md5, sha1, sha256, display_name)
6661  PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_IMAGE_INFO);
6662  preparedStatement.clearParameters();
6663  preparedStatement.setLong(1, newObjId);
6664  preparedStatement.setShort(2, (short) type.getValue());
6665  preparedStatement.setLong(3, sectorSize);
6666  preparedStatement.setString(4, timezone);
6667  //prevent negative size
6668  long savedSize = size < 0 ? 0 : size;
6669  preparedStatement.setLong(5, savedSize);
6670  preparedStatement.setString(6, md5);
6671  preparedStatement.setString(7, sha1);
6672  preparedStatement.setString(8, sha256);
6673  preparedStatement.setString(9, displayName);
6674  connection.executeUpdate(preparedStatement);
6675 
6676  // If there are paths, add them to tsk_image_names
6677  for (int i = 0; i < imagePaths.size(); i++) {
6678  preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_IMAGE_NAME);
6679  preparedStatement.clearParameters();
6680  preparedStatement.setLong(1, newObjId);
6681  preparedStatement.setString(2, imagePaths.get(i));
6682  preparedStatement.setLong(3, i);
6683  connection.executeUpdate(preparedStatement);
6684  }
6685 
6686  // Create the display name
6687  String name = displayName;
6688  if (name == null || name.isEmpty()) {
6689  if (imagePaths.size() > 0) {
6690  String path = imagePaths.get(0);
6691  name = (new java.io.File(path)).getName();
6692  } else {
6693  name = "";
6694  }
6695  }
6696 
6697  // Create a host if needed
6698  if (host == null) {
6699  if (name.isEmpty()) {
6700  host = getHostManager().newHost("Image_" + newObjId + " Host", transaction);
6701  } else {
6702  host = getHostManager().newHost(name + "_" + newObjId + " Host", transaction);
6703  }
6704  }
6705 
6706  // Add a row to data_source_info
6707  preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_DATA_SOURCE_INFO);
6708  statement = connection.createStatement();
6709  preparedStatement.setLong(1, newObjId);
6710  preparedStatement.setString(2, deviceId);
6711  preparedStatement.setString(3, timezone);
6712  preparedStatement.setLong(4, new Date().getTime());
6713  preparedStatement.setLong(5, host.getHostId());
6714  connection.executeUpdate(preparedStatement);
6715 
6716  // Create the new Image object
6717  return new Image(this, newObjId, type.getValue(), deviceId, sectorSize, name,
6718  imagePaths.toArray(new String[imagePaths.size()]), timezone, md5, sha1, sha256, savedSize);
6719  } catch (SQLException ex) {
6720  if (!imagePaths.isEmpty()) {
6721  throw new TskCoreException(String.format("Error adding image with path %s to database", imagePaths.get(0)), ex);
6722  } else {
6723  throw new TskCoreException(String.format("Error adding image with display name %s to database", displayName), ex);
6724  }
6725  } finally {
6726  closeStatement(statement);
6727  }
6728  }
6729 
6743  public VolumeSystem addVolumeSystem(long parentObjId, TskData.TSK_VS_TYPE_ENUM type, long imgOffset,
6744  long blockSize, CaseDbTransaction transaction) throws TskCoreException {
6745  try {
6746  // Insert a row for the VolumeSystem into the tsk_objects table.
6747  CaseDbConnection connection = transaction.getConnection();
6748  long newObjId = addObject(parentObjId, TskData.ObjectType.VS.getObjectType(), connection);
6749 
6750  // Add a row to tsk_vs_info
6751  // INSERT INTO tsk_vs_info (obj_id, vs_type, img_offset, block_size)
6752  PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_VS_INFO);
6753  preparedStatement.clearParameters();
6754  preparedStatement.setLong(1, newObjId);
6755  preparedStatement.setShort(2, (short) type.getVsType());
6756  preparedStatement.setLong(3, imgOffset);
6757  preparedStatement.setLong(4, blockSize);
6758  connection.executeUpdate(preparedStatement);
6759 
6760  // Create the new VolumeSystem object
6761  return new VolumeSystem(this, newObjId, "", type.getVsType(), imgOffset, blockSize);
6762  } catch (SQLException ex) {
6763  throw new TskCoreException(String.format("Error creating volume system with parent ID %d and image offset %d",
6764  parentObjId, imgOffset), ex);
6765  }
6766  }
6767 
6783  public Volume addVolume(long parentObjId, long addr, long start, long length, String desc,
6784  long flags, CaseDbTransaction transaction) throws TskCoreException {
6785  try {
6786  // Insert a row for the Volume into the tsk_objects table.
6787  CaseDbConnection connection = transaction.getConnection();
6788  long newObjId = addObject(parentObjId, TskData.ObjectType.VOL.getObjectType(), connection);
6789 
6790  // Add a row to tsk_vs_parts
6791  // INSERT INTO tsk_vs_parts (obj_id, addr, start, length, desc, flags)
6792  PreparedStatement preparedStatement;
6793  if (this.dbType == DbType.POSTGRESQL) {
6794  preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_VS_PART_POSTGRESQL);
6795  } else {
6796  preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_VS_PART_SQLITE);
6797  }
6798  preparedStatement.clearParameters();
6799  preparedStatement.setLong(1, newObjId);
6800  preparedStatement.setLong(2, addr);
6801  preparedStatement.setLong(3, start);
6802  preparedStatement.setLong(4, length);
6803  preparedStatement.setString(5, desc);
6804  preparedStatement.setShort(6, (short) flags);
6805  connection.executeUpdate(preparedStatement);
6806 
6807  // Create the new Volume object
6808  return new Volume(this, newObjId, addr, start, length, flags, desc);
6809  } catch (SQLException ex) {
6810  throw new TskCoreException(String.format("Error creating volume with address %d and parent ID %d", addr, parentObjId), ex);
6811  }
6812  }
6813 
6825  public Pool addPool(long parentObjId, TskData.TSK_POOL_TYPE_ENUM type, CaseDbTransaction transaction) throws TskCoreException {
6826  try {
6827  // Insert a row for the Pool into the tsk_objects table.
6828  CaseDbConnection connection = transaction.getConnection();
6829  long newObjId = addObject(parentObjId, TskData.ObjectType.POOL.getObjectType(), connection);
6830 
6831  // Add a row to tsk_pool_info
6832  // INSERT INTO tsk_pool_info (obj_id, pool_type) VALUES (?, ?)
6833  PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_POOL_INFO);
6834  preparedStatement.clearParameters();
6835  preparedStatement.setLong(1, newObjId);
6836  preparedStatement.setShort(2, type.getValue());
6837  connection.executeUpdate(preparedStatement);
6838 
6839  // Create the new Pool object
6840  return new Pool(this, newObjId, type.getName(), type.getValue());
6841  } catch (SQLException ex) {
6842  throw new TskCoreException(String.format("Error creating pool with type %d and parent ID %d", type.getValue(), parentObjId), ex);
6843  }
6844  }
6845 
6864  public FileSystem addFileSystem(long parentObjId, long imgOffset, TskData.TSK_FS_TYPE_ENUM type, long blockSize, long blockCount,
6865  long rootInum, long firstInum, long lastInum, String displayName,
6866  CaseDbTransaction transaction) throws TskCoreException {
6867  try {
6868  // Insert a row for the FileSystem into the tsk_objects table.
6869  CaseDbConnection connection = transaction.getConnection();
6870  long newObjId = addObject(parentObjId, TskData.ObjectType.FS.getObjectType(), connection);
6871 
6872  // Get the data source object ID
6873  long dataSourceId = getDataSourceObjectId(connection, newObjId);
6874 
6875  // Add a row to tsk_fs_info
6876  // INSERT INTO tsk_fs_info (obj_id, data_source_obj_id, img_offset, fs_type, block_size, block_count, root_inum, first_inum, last_inum, display_name)
6877  PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FS_INFO);
6878  preparedStatement.clearParameters();
6879  preparedStatement.setLong(1, newObjId);
6880  preparedStatement.setLong(2, dataSourceId);
6881  preparedStatement.setLong(3, imgOffset);
6882  preparedStatement.setInt(4, type.getValue());
6883  preparedStatement.setLong(5, blockSize);
6884  preparedStatement.setLong(6, blockCount);
6885  preparedStatement.setLong(7, rootInum);
6886  preparedStatement.setLong(8, firstInum);
6887  preparedStatement.setLong(9, lastInum);
6888  preparedStatement.setString(10, displayName);
6889  connection.executeUpdate(preparedStatement);
6890 
6891  // Create the new FileSystem object
6892  return new FileSystem(this, newObjId, displayName, imgOffset, type, blockSize, blockCount, rootInum,
6893  firstInum, lastInum);
6894  } catch (SQLException ex) {
6895  throw new TskCoreException(String.format("Error creating file system with image offset %d and parent ID %d",
6896  imgOffset, parentObjId), ex);
6897  }
6898  }
6899 
6925  public FsContent addFileSystemFile(long dataSourceObjId, long fsObjId,
6926  String fileName,
6927  long metaAddr, int metaSeq,
6928  TSK_FS_ATTR_TYPE_ENUM attrType, int attrId,
6929  TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size,
6930  long ctime, long crtime, long atime, long mtime,
6931  boolean isFile, Content parent) throws TskCoreException {
6932 
6933  CaseDbTransaction transaction = beginTransaction();
6934  try {
6935 
6936  FsContent fileSystemFile = addFileSystemFile(dataSourceObjId, fsObjId, fileName,
6937  metaAddr, metaSeq, attrType, attrId, dirFlag, metaFlags, size,
6938  ctime, crtime, atime, mtime, null, null, null, isFile, parent,
6939  OsAccount.NO_OWNER_ID, null,
6940  Collections.emptyList(), transaction);
6941 
6942  transaction.commit();
6943  transaction = null;
6944  return fileSystemFile;
6945  } finally {
6946  if (null != transaction) {
6947  try {
6948  transaction.rollback();
6949  } catch (TskCoreException ex2) {
6950  logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex2);
6951  }
6952  }
6953  }
6954  }
6955 
6993  public FsContent addFileSystemFile(long dataSourceObjId, long fsObjId,
6994  String fileName,
6995  long metaAddr, int metaSeq,
6996  TSK_FS_ATTR_TYPE_ENUM attrType, int attrId,
6997  TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size,
6998  long ctime, long crtime, long atime, long mtime,
6999  String md5Hash, String sha256Hash, String mimeType,
7000  boolean isFile, Content parent, String ownerUid,
7001  OsAccount osAccount, List<Attribute> fileAttributes,
7002  CaseDbTransaction transaction) throws TskCoreException {
7003 
7004  return addFileSystemFile(dataSourceObjId, fsObjId,
7005  fileName,
7006  metaAddr, metaSeq,
7007  attrType, attrId,
7008  dirFlag, metaFlags, size,
7009  ctime, crtime, atime, mtime,
7010  md5Hash, sha256Hash, null,
7011  mimeType,
7012  isFile, parent, ownerUid,
7013  osAccount, fileAttributes,
7014  transaction);
7015  }
7016