Sleuth Kit Java Bindings (JNI)  4.3
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-2017 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.mchange.v2.c3p0.ComboPooledDataSource;
22 import com.mchange.v2.c3p0.DataSources;
23 import com.mchange.v2.c3p0.PooledDataSource;
24 import java.beans.PropertyVetoException;
25 import java.io.BufferedInputStream;
26 import java.io.BufferedOutputStream;
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.FileOutputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.io.UnsupportedEncodingException;
34 import java.net.InetAddress;
35 import java.net.URLEncoder;
36 import java.nio.charset.StandardCharsets;
37 import java.nio.file.Paths;
38 import java.sql.Connection;
39 import java.sql.DriverManager;
40 import java.sql.PreparedStatement;
41 import java.sql.ResultSet;
42 import java.sql.SQLException;
43 import java.sql.Statement;
44 import java.text.SimpleDateFormat;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Collection;
48 import java.util.Collections;
49 import java.util.Date;
50 import java.util.EnumMap;
51 import java.util.HashMap;
52 import java.util.HashSet;
53 import java.util.LinkedHashMap;
54 import java.util.List;
55 import java.util.Map;
56 import java.util.MissingResourceException;
57 import java.util.ResourceBundle;
58 import java.util.Set;
59 import java.util.UUID;
60 import java.util.concurrent.ConcurrentHashMap;
61 import java.util.concurrent.locks.ReentrantReadWriteLock;
62 import java.util.logging.Level;
63 import java.util.logging.Logger;
64 import org.postgresql.util.PSQLException;
65 import org.postgresql.util.PSQLState;
80 import org.sqlite.SQLiteConfig;
81 import org.sqlite.SQLiteDataSource;
82 import org.sqlite.SQLiteJDBCLoader;
83 
88 public class SleuthkitCase {
89 
90  private static final int MAX_DB_NAME_LEN_BEFORE_TIMESTAMP = 47;
91  private static final int SCHEMA_VERSION_NUMBER = 6; // This must be the same as TSK_SCHEMA_VER in tsk/auto/tsk_db.h.
92  private static final long BASE_ARTIFACT_ID = Long.MIN_VALUE; // Artifact ids will start at the lowest negative value
93  private static final Logger logger = Logger.getLogger(SleuthkitCase.class.getName());
94  private static final ResourceBundle bundle = ResourceBundle.getBundle("org.sleuthkit.datamodel.Bundle");
95  private static final int IS_REACHABLE_TIMEOUT_MS = 1000;
96  private static final String SQL_ERROR_CONNECTION_GROUP = "08";
97  private static final String SQL_ERROR_AUTHENTICATION_GROUP = "28";
98  private static final String SQL_ERROR_PRIVILEGE_GROUP = "42";
99  private static final String SQL_ERROR_RESOURCE_GROUP = "53";
100  private static final String SQL_ERROR_LIMIT_GROUP = "54";
101  private static final String SQL_ERROR_INTERNAL_GROUP = "xx";
102  private static final int MIN_USER_DEFINED_TYPE_ID = 10000;
104  private final Map<Long, VirtualDirectory> rootIdsToCarvedFileDirs = new HashMap<Long, VirtualDirectory>();
105  private final Map<Long, FileSystem> fileSystemIdMap = new HashMap<Long, FileSystem>(); // Cache for file system files.
106  private final ArrayList<ErrorObserver> sleuthkitCaseErrorObservers = new ArrayList<ErrorObserver>();
107  private final String databaseName;
108  private final String dbPath;
109  private final DbType dbType;
110  private final String caseDirPath;
112  private int versionNumber;
113  private String dbBackupPath;
118  private long nextArtifactId; // Used to ensure artifact ids come from the desired range.
119  // This read/write lock is used to implement a layer of locking on top of
120  // the locking protocol provided by the underlying SQLite database. The Java
121  // locking protocol improves performance for reasons that are not currently
122  // understood. Note that the lock is contructed to use a fairness policy.
123  private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true);
124 
139  public static void tryConnect(CaseDbConnectionInfo info) throws TskCoreException {
140  // Check if we can talk to the database.
141  if (info.getHost() == null || info.getHost().isEmpty()) {
142  throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.MissingHostname")); //NON-NLS
143  } else if (info.getPort() == null || info.getPort().isEmpty()) {
144  throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.MissingPort")); //NON-NLS
145  } else if (info.getUserName() == null || info.getUserName().isEmpty()) {
146  throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.MissingUsername")); //NON-NLS
147  } else if (info.getPassword() == null || info.getPassword().isEmpty()) {
148  throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.MissingPassword")); //NON-NLS
149  }
150 
151  try {
152  Class.forName("org.postgresql.Driver"); //NON-NLS
153  Connection conn = DriverManager.getConnection("jdbc:postgresql://" + info.getHost() + ":" + info.getPort() + "/postgres", info.getUserName(), info.getPassword()); //NON-NLS
154  if (conn != null) {
155  conn.close();
156  }
157  } catch (SQLException ex) {
158  String result;
159  String sqlState = ex.getSQLState().toLowerCase();
160  if (sqlState.startsWith(SQL_ERROR_CONNECTION_GROUP)) {
161  try {
162  if (InetAddress.getByName(info.getHost()).isReachable(IS_REACHABLE_TIMEOUT_MS)) {
163  // if we can reach the host, then it's probably port problem
164  result = bundle.getString("DatabaseConnectionCheck.Port"); //NON-NLS
165  } else {
166  result = bundle.getString("DatabaseConnectionCheck.HostnameOrPort"); //NON-NLS
167  }
168  } catch (IOException any) {
169  // it may be anything
170  result = bundle.getString("DatabaseConnectionCheck.Everything"); //NON-NLS
171  } catch (MissingResourceException any) {
172  // it may be anything
173  result = bundle.getString("DatabaseConnectionCheck.Everything"); //NON-NLS
174  }
175  } else if (sqlState.startsWith(SQL_ERROR_AUTHENTICATION_GROUP)) {
176  result = bundle.getString("DatabaseConnectionCheck.Authentication"); //NON-NLS
177  } else if (sqlState.startsWith(SQL_ERROR_PRIVILEGE_GROUP)) {
178  result = bundle.getString("DatabaseConnectionCheck.Access"); //NON-NLS
179  } else if (sqlState.startsWith(SQL_ERROR_RESOURCE_GROUP)) {
180  result = bundle.getString("DatabaseConnectionCheck.ServerDiskSpace"); //NON-NLS
181  } else if (sqlState.startsWith(SQL_ERROR_LIMIT_GROUP)) {
182  result = bundle.getString("DatabaseConnectionCheck.ServerRestart"); //NON-NLS
183  } else if (sqlState.startsWith(SQL_ERROR_INTERNAL_GROUP)) {
184  result = bundle.getString("DatabaseConnectionCheck.InternalServerIssue"); //NON-NLS
185  } else {
186  result = bundle.getString("DatabaseConnectionCheck.Connection"); //NON-NLS
187  }
188  throw new TskCoreException(result);
189  } catch (ClassNotFoundException ex) {
190  throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.Installation")); //NON-NLS
191  }
192  }
193 
205  private SleuthkitCase(String dbPath, SleuthkitJNI.CaseDbHandle caseHandle, DbType dbType) throws Exception {
206  Class.forName("org.sqlite.JDBC");
207  this.dbPath = dbPath;
208  this.dbType = dbType;
209  File dbFile = new File(dbPath);
210  this.caseDirPath = dbFile.getParentFile().getAbsolutePath();
211  this.databaseName = dbFile.getName();
212  this.connections = new SQLiteConnections(dbPath);
213  this.caseHandle = caseHandle;
214  init();
216  }
217 
235  private SleuthkitCase(String host, int port, String dbName, String userName, String password, SleuthkitJNI.CaseDbHandle caseHandle, String caseDirPath, DbType dbType) throws Exception {
236  this.dbPath = "";
237  this.databaseName = dbName;
238  this.dbType = dbType;
239  this.caseDirPath = caseDirPath;
240  this.connections = new PostgreSQLConnections(host, port, dbName, userName, password);
241  this.caseHandle = caseHandle;
242  init();
243  }
244 
245  private void init() throws Exception {
246  typeIdToArtifactTypeMap = new ConcurrentHashMap<Integer, BlackboardArtifact.Type>();
247  typeIdToAttributeTypeMap = new ConcurrentHashMap<Integer, BlackboardAttribute.Type>();
248  typeNameToArtifactTypeMap = new ConcurrentHashMap<String, BlackboardArtifact.Type>();
249  typeNameToAttributeTypeMap = new ConcurrentHashMap<String, BlackboardAttribute.Type>();
250 
251  /*
252  * The following methods need to be called before updateDatabaseSchema
253  * due to the way that updateFromSchema2toSchema3 was implemented.
254  */
258 
259  updateDatabaseSchema(null);
260 
262 
263  CaseDbConnection connection = connections.getConnection();
264  initIngestModuleTypes(connection);
265  initIngestStatusTypes(connection);
266  initReviewStatuses(connection);
267  initEncodingTypes(connection);
268  connection.close();
269  }
270 
277  private void initBlackboardArtifactTypes() throws SQLException, TskCoreException {
278  CaseDbConnection connection = connections.getConnection();
279  Statement statement = null;
280  ResultSet resultSet = null;
281  try {
282  statement = connection.createStatement();
283  for (ARTIFACT_TYPE type : ARTIFACT_TYPE.values()) {
284  try {
285  statement.execute("INSERT INTO blackboard_artifact_types (artifact_type_id, type_name, display_name) VALUES (" + type.getTypeID() + " , '" + type.getLabel() + "', '" + type.getDisplayName() + "')"); //NON-NLS
286  } catch (SQLException ex) {
287  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM blackboard_artifact_types WHERE artifact_type_id = '" + type.getTypeID() + "'"); //NON-NLS
288  resultSet.next();
289  if (resultSet.getLong("count") == 0) {
290  throw ex;
291  }
292  resultSet.close();
293  resultSet = null;
294  }
295  this.typeIdToArtifactTypeMap.put(type.getTypeID(), new BlackboardArtifact.Type(type));
296  this.typeNameToArtifactTypeMap.put(type.getLabel(), new BlackboardArtifact.Type(type));
297  }
298  if (dbType == DbType.POSTGRESQL) {
299  int newPrimaryKeyIndex = Collections.max(Arrays.asList(ARTIFACT_TYPE.values())).getTypeID() + 1;
300  statement.execute("ALTER SEQUENCE blackboard_artifact_types_artifact_type_id_seq RESTART WITH " + newPrimaryKeyIndex); //NON-NLS
301  }
302  } finally {
303  closeResultSet(resultSet);
304  closeStatement(statement);
305  connection.close();
306  }
307  }
308 
316  private void initBlackboardAttributeTypes() throws SQLException, TskCoreException {
317  CaseDbConnection connection = connections.getConnection();
318  Statement statement = null;
319  ResultSet resultSet = null;
320  try {
321  statement = connection.createStatement();
322  for (ATTRIBUTE_TYPE type : ATTRIBUTE_TYPE.values()) {
323  try {
324  statement.execute("INSERT INTO blackboard_attribute_types (attribute_type_id, type_name, display_name, value_type) VALUES (" + type.getTypeID() + ", '" + type.getLabel() + "', '" + type.getDisplayName() + "', '" + type.getValueType().getType() + "')"); //NON-NLS
325  } catch (SQLException ex) {
326  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM blackboard_attribute_types WHERE attribute_type_id = '" + type.getTypeID() + "'"); //NON-NLS
327  resultSet.next();
328  if (resultSet.getLong("count") == 0) {
329  throw ex;
330  }
331  resultSet.close();
332  resultSet = null;
333  }
334  this.typeIdToAttributeTypeMap.put(type.getTypeID(), new BlackboardAttribute.Type(type));
335  this.typeNameToAttributeTypeMap.put(type.getLabel(), new BlackboardAttribute.Type(type));
336  }
337  if (this.dbType == DbType.POSTGRESQL) {
338  int newPrimaryKeyIndex = Collections.max(Arrays.asList(ATTRIBUTE_TYPE.values())).getTypeID() + 1;
339  statement.execute("ALTER SEQUENCE blackboard_attribute_types_attribute_type_id_seq RESTART WITH " + newPrimaryKeyIndex); //NON-NLS
340  }
341  } finally {
342  closeResultSet(resultSet);
343  closeStatement(statement);
344  connection.close();
345  }
346  }
347 
357  private void initNextArtifactId() throws SQLException, TskCoreException {
358  CaseDbConnection connection = connections.getConnection();
359  Statement statement = null;
360  ResultSet resultSet = null;
361  try {
362  statement = connection.createStatement();
363  resultSet = connection.executeQuery(statement, "SELECT MAX(artifact_id) AS max_artifact_id FROM blackboard_artifacts"); //NON-NLS
364  resultSet.next();
365  this.nextArtifactId = resultSet.getLong("max_artifact_id") + 1;
366  if (this.nextArtifactId == 1) {
367  this.nextArtifactId = BASE_ARTIFACT_ID;
368  }
369  } finally {
370  closeResultSet(resultSet);
371  closeStatement(statement);
372  connection.close();
373  }
374  }
375 
383  private void initStandardTagNames() throws SQLException, TskCoreException {
384  String bookmarkDisplayName = bundle.getString("SleuthkitCase.initStandardTagNames.bookmark.text");
385  CaseDbConnection connection = connections.getConnection();
386  Statement statement = null;
387  ResultSet resultSet = null;
388  try {
389  statement = connection.createStatement();
390  statement.execute("INSERT INTO tag_names (display_name, description, color) VALUES ('" + bookmarkDisplayName + "', '', '" + TagName.HTML_COLOR.NONE.getName() + "');"); //NON-NLS
391  } catch (SQLException ex) {
392  /*
393  * If the exception is a violation of the tag names uniqueness
394  * constraint, it can be ignored because another user has added the
395  * tag name. Otherwise, propagate the exception.
396  */
397  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM tag_names WHERE display_name = '" + bookmarkDisplayName + "'"); //NON-NLS
398  resultSet.next();
399  if (resultSet.getLong("count") == 0) {
400  throw ex;
401  }
402  } finally {
403  closeResultSet(resultSet);
404  closeStatement(statement);
405  connection.close();
406  }
407  }
408 
416  private void initIngestModuleTypes(CaseDbConnection connection) throws SQLException, TskCoreException {
417  Statement statement = null;
418  ResultSet resultSet = null;
419  try {
420  statement = connection.createStatement();
421  for (IngestModuleType type : IngestModuleType.values()) {
422  try {
423  statement.execute("INSERT INTO ingest_module_types (type_id, type_name) VALUES (" + type.ordinal() + ", '" + type.toString() + "');"); //NON-NLS
424  } catch (SQLException ex) {
425  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) as count FROM ingest_module_types WHERE type_id = " + type.ordinal() + ";"); //NON-NLS
426  resultSet.next();
427  if (resultSet.getLong("count") == 0) {
428  throw ex;
429  }
430  resultSet.close();
431  resultSet = null;
432  }
433  }
434  } finally {
435  closeResultSet(resultSet);
436  closeStatement(statement);
437  }
438  }
439 
447  private void initIngestStatusTypes(CaseDbConnection connection) throws SQLException, TskCoreException {
448  Statement statement = null;
449  ResultSet resultSet = null;
450  try {
451  statement = connection.createStatement();
452  for (IngestJobStatusType type : IngestJobStatusType.values()) {
453  try {
454  statement.execute("INSERT INTO ingest_job_status_types (type_id, type_name) VALUES (" + type.ordinal() + ", '" + type.toString() + "');"); //NON-NLS
455  } catch (SQLException ex) {
456  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) as count FROM ingest_job_status_types WHERE type_id = " + type.ordinal() + ";"); //NON-NLS
457  resultSet.next();
458  if (resultSet.getLong("count") == 0) {
459  throw ex;
460  }
461  resultSet.close();
462  resultSet = null;
463  }
464  }
465  } finally {
466  closeResultSet(resultSet);
467  closeStatement(statement);
468  }
469  }
470 
477  private void initReviewStatuses(CaseDbConnection connection) throws SQLException, TskCoreException {
478  Statement statement = null;
479  ResultSet resultSet = null;
480  try {
481  statement = connection.createStatement();
483  try {
484  statement.execute("INSERT INTO review_statuses (review_status_id, review_status_name, display_name) " //NON-NLS
485  + "VALUES (" + status.getID() + ",'" + status.getName() + "','" + status.getDisplayName() + "')"); //NON-NLS
486  } catch (SQLException ex) {
487  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) as count FROM review_statuses WHERE review_status_id = " + status.getID()); //NON-NLS
488  resultSet.next();
489  if (resultSet.getLong("count") == 0) {
490  throw ex;
491  }
492  resultSet.close();
493  resultSet = null;
494  }
495  }
496  } finally {
497  closeResultSet(resultSet);
498  closeStatement(statement);
499  }
500  }
501 
509  private void initEncodingTypes(CaseDbConnection connection) throws SQLException, TskCoreException {
510  Statement statement = null;
511  ResultSet resultSet = null;
512  try {
513  statement = connection.createStatement();
514  for (TskData.EncodingType type : TskData.EncodingType.values()) {
515  try {
516  statement.execute("INSERT INTO file_encoding_types (encoding_type, name) VALUES (" + type.getType() + " , '" + type.name() + "')"); //NON-NLS
517  } catch (SQLException ex) {
518  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) as count FROM file_encoding_types WHERE encoding_type = " + type.getType()); //NON-NLS
519  resultSet.next();
520  if (resultSet.getLong("count") == 0) {
521  throw ex;
522  }
523  resultSet.close();
524  resultSet = null;
525  }
526  }
527  } finally {
528  closeResultSet(resultSet);
529  closeStatement(statement);
530  }
531  }
532 
539  private void updateDatabaseSchema(String dbPath) throws Exception {
540  CaseDbConnection connection = connections.getConnection();
541  ResultSet resultSet = null;
542  Statement statement = null;
543  try {
544  connection.beginTransaction();
545 
546  // Get the schema version number of the case database from the tsk_db_info table.
547  int schemaVersionNumber = SCHEMA_VERSION_NUMBER;
548  statement = connection.createStatement();
549  resultSet = connection.executeQuery(statement, "SELECT schema_ver FROM tsk_db_info"); //NON-NLS
550  if (resultSet.next()) {
551  schemaVersionNumber = resultSet.getInt("schema_ver"); //NON-NLS
552  }
553  resultSet.close();
554  resultSet = null;
555  statement.close();
556  statement = null;
557 
558  // Do the schema update(s), if needed.
559  if (SCHEMA_VERSION_NUMBER != schemaVersionNumber) {
560  if (null != dbPath) {
561  // Make a backup copy of the database. Client code can get the path of the backup
562  // using the getBackupDatabasePath() method.
563  String backupFilePath = dbPath + ".schemaVer" + schemaVersionNumber + ".backup"; //NON-NLS
564  copyCaseDB(backupFilePath);
565  dbBackupPath = backupFilePath;
566  }
567 
568  // ***CALL SCHEMA UPDATE METHODS HERE***
569  // Each method should examine the schema number passed to it and either:
570  // a. do nothing and return the schema version number unchanged, or
571  // b. upgrade the database and then increment and return the schema version number.
572  schemaVersionNumber = updateFromSchema2toSchema3(schemaVersionNumber, connection);
573  schemaVersionNumber = updateFromSchema3toSchema4(schemaVersionNumber, connection);
574  schemaVersionNumber = updateFromSchema4toSchema5(schemaVersionNumber, connection);
575  schemaVersionNumber = updateFromSchema5toSchema6(schemaVersionNumber, connection);
576 
577  // Write the updated schema version number to the the tsk_db_info table.
578  statement = connection.createStatement();
579  connection.executeUpdate(statement, "UPDATE tsk_db_info SET schema_ver = " + schemaVersionNumber); //NON-NLS
580  statement.close();
581  statement = null;
582  }
583  versionNumber = schemaVersionNumber;
584 
585  connection.commitTransaction();
586  } catch (Exception ex) { // Cannot do exception multi-catch in Java 6, so use catch-all.
587  connection.rollbackTransaction();
588  throw ex;
589  } finally {
590  closeResultSet(resultSet);
591  closeStatement(statement);
592  connection.close();
593  }
594  }
595 
605  public void copyCaseDB(String newDBPath) throws IOException {
606  if (dbPath.isEmpty()) {
607  throw new IOException("Copying case database files is not supported for this type of case database"); //NON-NLS
608  }
609  InputStream in = null;
610  OutputStream out = null;
612  try {
613  InputStream inFile = new FileInputStream(dbPath);
614  in = new BufferedInputStream(inFile);
615  OutputStream outFile = new FileOutputStream(newDBPath);
616  out = new BufferedOutputStream(outFile);
617  int bytesRead = in.read();
618  while (bytesRead != -1) {
619  out.write(bytesRead);
620  bytesRead = in.read();
621  }
622  } finally {
623  try {
624  if (in != null) {
625  in.close();
626  }
627  if (out != null) {
628  out.flush();
629  out.close();
630  }
631  } catch (IOException e) {
632  logger.log(Level.WARNING, "Could not close streams after db copy", e); //NON-NLS
633  }
635  }
636  }
637 
641  private void logSQLiteJDBCDriverInfo() {
642  try {
643  SleuthkitCase.logger.info(String.format("sqlite-jdbc version %s loaded in %s mode", //NON-NLS
644  SQLiteJDBCLoader.getVersion(), SQLiteJDBCLoader.isNativeMode()
645  ? "native" : "pure-java")); //NON-NLS
646  } catch (Exception ex) {
647  SleuthkitCase.logger.log(Level.SEVERE, "Error querying case database mode", ex);
648  }
649  }
650 
665  @SuppressWarnings("deprecation")
666  private int updateFromSchema2toSchema3(int schemaVersionNumber, CaseDbConnection connection) throws SQLException, TskCoreException {
667  if (schemaVersionNumber != 2) {
668  return schemaVersionNumber;
669  }
670  Statement statement = null;
671  Statement updateStatement = null;
672  ResultSet resultSet = null;
673  try {
674  statement = connection.createStatement();
675 
676  // Add new tables for tags.
677  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
678  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
679  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
680 
681  // Add a new table for reports.
682  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
683 
684  // Add new columns to the image info table.
685  statement.execute("ALTER TABLE tsk_image_info ADD COLUMN size INTEGER;"); //NON-NLS
686  statement.execute("ALTER TABLE tsk_image_info ADD COLUMN md5 TEXT;"); //NON-NLS
687  statement.execute("ALTER TABLE tsk_image_info ADD COLUMN display_name TEXT;"); //NON-NLS
688 
689  // Add a new column to the file system info table.
690  statement.execute("ALTER TABLE tsk_fs_info ADD COLUMN display_name TEXT;"); //NON-NLS
691 
692  // Add a new column to the file table.
693  statement.execute("ALTER TABLE tsk_files ADD COLUMN meta_seq INTEGER;"); //NON-NLS
694 
695  // Add new columns and indexes to the attributes table and populate the
696  // new column. Note that addition of the new column is a denormalization
697  // to optimize attribute queries.
698  statement.execute("ALTER TABLE blackboard_attributes ADD COLUMN artifact_type_id INTEGER NULL NOT NULL DEFAULT -1;"); //NON-NLS
699  statement.execute("CREATE INDEX attribute_artifactTypeId ON blackboard_attributes(artifact_type_id);"); //NON-NLS
700  statement.execute("CREATE INDEX attribute_valueText ON blackboard_attributes(value_text);"); //NON-NLS
701  statement.execute("CREATE INDEX attribute_valueInt32 ON blackboard_attributes(value_int32);"); //NON-NLS
702  statement.execute("CREATE INDEX attribute_valueInt64 ON blackboard_attributes(value_int64);"); //NON-NLS
703  statement.execute("CREATE INDEX attribute_valueDouble ON blackboard_attributes(value_double);"); //NON-NLS
704  resultSet = statement.executeQuery("SELECT attrs.artifact_id AS artifact_id, " //NON-NLS
705  + "arts.artifact_type_id AS artifact_type_id " //NON-NLS
706  + "FROM blackboard_attributes AS attrs " //NON-NLS
707  + "INNER JOIN blackboard_artifacts AS arts " //NON-NLS
708  + "WHERE attrs.artifact_id = arts.artifact_id;"); //NON-NLS
709  updateStatement = connection.createStatement();
710  while (resultSet.next()) {
711  long artifactId = resultSet.getLong("artifact_id");
712  int artifactTypeId = resultSet.getInt("artifact_type_id");
713  updateStatement.executeUpdate(
714  "UPDATE blackboard_attributes " //NON-NLS
715  + "SET artifact_type_id = " + artifactTypeId //NON-NLS
716  + " WHERE blackboard_attributes.artifact_id = " + artifactId + ";"); //NON-NLS
717  }
718  resultSet.close();
719  resultSet = null;
720 
721  // Convert existing tag artifact and attribute rows to rows in the new tags tables.
722  // TODO: This code depends on prepared statements that could evolve with
723  // time, breaking this upgrade. The code that follows should be rewritten
724  // to do everything with SQL specific to case database schema version 2.
725  HashMap<String, TagName> tagNames = new HashMap<String, TagName>();
727  Content content = getContentById(artifact.getObjectID());
728  String name = ""; //NON-NLS
729  String comment = ""; //NON-NLS
730  ArrayList<BlackboardAttribute> attributes = getBlackboardAttributes(artifact);
731  for (BlackboardAttribute attribute : attributes) {
732  if (attribute.getAttributeTypeID() == ATTRIBUTE_TYPE.TSK_TAG_NAME.getTypeID()) {
733  name = attribute.getValueString();
734  } else if (attribute.getAttributeTypeID() == ATTRIBUTE_TYPE.TSK_COMMENT.getTypeID()) {
735  comment = attribute.getValueString();
736  }
737  }
738  if (!name.isEmpty()) {
739  TagName tagName;
740  if (tagNames.containsKey(name)) {
741  tagName = tagNames.get(name);
742  } else {
743  tagName = addTagName(name, "", TagName.HTML_COLOR.NONE); //NON-NLS
744  tagNames.put(name, tagName);
745  }
746  addContentTag(content, tagName, comment, 0, content.getSize() - 1);
747  }
748  }
750  long taggedArtifactId = -1;
751  String name = ""; //NON-NLS
752  String comment = ""; //NON-NLS
753  ArrayList<BlackboardAttribute> attributes = getBlackboardAttributes(artifact);
754  for (BlackboardAttribute attribute : attributes) {
755  if (attribute.getAttributeTypeID() == ATTRIBUTE_TYPE.TSK_TAG_NAME.getTypeID()) {
756  name = attribute.getValueString();
757  } else if (attribute.getAttributeTypeID() == ATTRIBUTE_TYPE.TSK_COMMENT.getTypeID()) {
758  comment = attribute.getValueString();
759  } else if (attribute.getAttributeTypeID() == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()) {
760  taggedArtifactId = attribute.getValueLong();
761  }
762  }
763  if (taggedArtifactId != -1 && !name.isEmpty()) {
764  TagName tagName;
765  if (tagNames.containsKey(name)) {
766  tagName = tagNames.get(name);
767  } else {
768  tagName = addTagName(name, "", TagName.HTML_COLOR.NONE); //NON-NLS
769  tagNames.put(name, tagName);
770  }
771  addBlackboardArtifactTag(getBlackboardArtifact(taggedArtifactId), tagName, comment);
772  }
773  }
774  statement.execute(
775  "DELETE FROM blackboard_attributes WHERE artifact_id IN " //NON-NLS
776  + "(SELECT artifact_id FROM blackboard_artifacts WHERE artifact_type_id = " //NON-NLS
777  + ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID()
778  + " OR artifact_type_id = " + ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID() + ");"); //NON-NLS
779  statement.execute(
780  "DELETE FROM blackboard_artifacts WHERE artifact_type_id = " //NON-NLS
781  + ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID()
782  + " OR artifact_type_id = " + ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID() + ";"); //NON-NLS
783 
784  return 3;
785  } finally {
786  closeStatement(updateStatement);
787  closeResultSet(resultSet);
788  closeStatement(statement);
789  connection.close();
790  }
791  }
792 
807  private int updateFromSchema3toSchema4(int schemaVersionNumber, CaseDbConnection connection) throws SQLException, TskCoreException {
808  if (schemaVersionNumber != 3) {
809  return schemaVersionNumber;
810  }
811 
812  Statement statement = null;
813  ResultSet resultSet = null;
814  Statement queryStatement = null;
815  ResultSet queryResultSet = null;
816  Statement updateStatement = null;
817  try {
818  // Add mime_type column to tsk_files table. Populate with general
819  // info artifact file signature data.
820  statement = connection.createStatement();
821  updateStatement = connection.createStatement();
822  statement.execute("ALTER TABLE tsk_files ADD COLUMN mime_type TEXT;");
823  resultSet = statement.executeQuery("SELECT files.obj_id AS obj_id, attrs.value_text AS value_text "
824  + "FROM tsk_files AS files, blackboard_attributes AS attrs, blackboard_artifacts AS arts "
825  + "WHERE files.obj_id = arts.obj_id AND "
826  + "arts.artifact_id = attrs.artifact_id AND "
827  + "arts.artifact_type_id = 1 AND "
828  + "attrs.attribute_type_id = 62");
829  while (resultSet.next()) {
830  updateStatement.executeUpdate(
831  "UPDATE tsk_files " //NON-NLS
832  + "SET mime_type = '" + resultSet.getString("value_text") + "' " //NON-NLS
833  + "WHERE tsk_files.obj_id = " + resultSet.getInt("obj_id") + ";"); //NON-NLS
834  }
835  resultSet.close();
836 
837  // Add value_type column to blackboard_attribute_types table.
838  statement.execute("ALTER TABLE blackboard_attribute_types ADD COLUMN value_type INTEGER NOT NULL DEFAULT -1;");
839  resultSet = statement.executeQuery("SELECT * FROM blackboard_attribute_types AS types"); //NON-NLS
840  while (resultSet.next()) {
841  int attributeTypeId = resultSet.getInt("attribute_type_id");
842  String attributeLabel = resultSet.getString("type_name");
843  if (attributeTypeId < MIN_USER_DEFINED_TYPE_ID) {
844  updateStatement.executeUpdate(
845  "UPDATE blackboard_attribute_types " //NON-NLS
846  + "SET value_type = " + ATTRIBUTE_TYPE.fromLabel(attributeLabel).getValueType().getType() + " " //NON-NLS
847  + "WHERE blackboard_attribute_types.attribute_type_id = " + attributeTypeId + ";"); //NON-NLS
848  }
849  }
850  resultSet.close();
851 
852  // Add a data_sources_info table.
853  queryStatement = connection.createStatement();
854  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));");
855  resultSet = statement.executeQuery("SELECT * FROM tsk_objects WHERE par_obj_id IS NULL");
856  while (resultSet.next()) {
857  long objectId = resultSet.getLong("obj_id");
858  String timeZone = "";
859  queryResultSet = queryStatement.executeQuery("SELECT tzone FROM tsk_image_info WHERE obj_id = " + objectId);
860  if (queryResultSet.next()) {
861  timeZone = queryResultSet.getString("tzone");
862  }
863  queryResultSet.close();
864  updateStatement.executeUpdate("INSERT INTO data_source_info (obj_id, device_id, time_zone) "
865  + "VALUES(" + objectId + ", '" + UUID.randomUUID().toString() + "' , '" + timeZone + "');");
866  }
867  resultSet.close();
868 
869  // Add data_source_obj_id column to the tsk_files table.
870  //
871  // NOTE: A new case database will have the following FK constraint:
872  //
873  // REFERENCES data_source_info (obj_id)
874  //
875  // The constraint is sacrificed here to avoid having to create and
876  // populate a new tsk_files table.
877  //
878  // TODO: Do this right.
879  statement.execute("ALTER TABLE tsk_files ADD COLUMN data_source_obj_id BIGINT NOT NULL DEFAULT -1;");
880  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");
881  while (resultSet.next()) {
882  long fileId = resultSet.getLong("obj_id");
883  long dataSourceId = getDataSourceObjectId(connection, fileId);
884  updateStatement.executeUpdate("UPDATE tsk_files SET data_source_obj_id = " + dataSourceId + " WHERE obj_id = " + fileId + ";");
885  }
886  resultSet.close();
887  statement.execute("CREATE TABLE ingest_module_types (type_id INTEGER PRIMARY KEY, type_name TEXT NOT NULL)"); //NON-NLS
888  statement.execute("CREATE TABLE ingest_job_status_types (type_id INTEGER PRIMARY KEY, type_name TEXT NOT NULL)"); //NON-NLS
889  if (this.dbType.equals(DbType.SQLITE)) {
890  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
891  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
892  } else {
893  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
894  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
895  }
896 
897  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
898  initIngestModuleTypes(connection);
899  initIngestStatusTypes(connection);
900 
901  return 4;
902 
903  } finally {
904  closeResultSet(queryResultSet);
905  closeStatement(queryStatement);
906  closeStatement(updateStatement);
907  closeResultSet(resultSet);
908  closeStatement(statement);
909  }
910  }
911 
926  private int updateFromSchema4toSchema5(int schemaVersionNumber, CaseDbConnection connection) throws SQLException, TskCoreException {
927  if (schemaVersionNumber != 4) {
928  return schemaVersionNumber;
929  }
930 
931  Statement statement = null;
932  try {
933  // Add the review_statuses lookup table.
934  statement = connection.createStatement();
935  statement.execute("CREATE TABLE review_statuses (review_status_id INTEGER PRIMARY KEY, review_status_name TEXT NOT NULL, display_name TEXT NOT NULL)");
936 
937  /*
938  * Add review_status_id column to artifacts table.
939  *
940  * NOTE: For DBs created with schema 5 we define a foreign key
941  * constraint on the review_status_column. We don't bother with this
942  * for DBs updated to schema 5 because of limitations of the SQLite
943  * ALTER TABLE command.
944  */
945  statement.execute("ALTER TABLE blackboard_artifacts ADD COLUMN review_status_id INTEGER NOT NULL DEFAULT " + BlackboardArtifact.ReviewStatus.UNDECIDED.getID());
946 
947  // Add the encoding table
948  statement.execute("CREATE TABLE file_encoding_types (encoding_type INTEGER PRIMARY KEY, name TEXT NOT NULL);");
949  initEncodingTypes(connection);
950 
951  /*
952  * This needs to be done due to a Autopsy/TSK out of synch problem.
953  * Without this, it is possible to upgrade from version 4 to 5 and
954  * then 5 to 6, but not from 4 to 6.
955  */
956  initReviewStatuses(connection);
957 
958  // Add encoding type column to tsk_files_path
959  // This should really have the FOREIGN KEY constraint but there are problems
960  // getting that to work, so we don't add it on this upgrade path.
961  statement.execute("ALTER TABLE tsk_files_path ADD COLUMN encoding_type INTEGER NOT NULL DEFAULT 0;");
962 
963  return 5;
964 
965  } finally {
966  closeStatement(statement);
967  }
968  }
969 
984  private int updateFromSchema5toSchema6(int schemaVersionNumber, CaseDbConnection connection) throws SQLException, TskCoreException {
985  if (schemaVersionNumber != 5) {
986  return schemaVersionNumber;
987  }
988 
989  /*
990  * This upgrade fixes a bug where some releases had artifact review
991  * status support in the case database and others did not.
992  */
993  Statement statement = null;
994  ResultSet resultSet = null;
995  try {
996  /*
997  * Add the review_statuses lookup table, if missing.
998  */
999  statement = connection.createStatement();
1000  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)");
1001 
1002  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM review_statuses"); //NON-NLS
1003  resultSet.next();
1004  if (resultSet.getLong("count") == 0) {
1005  /*
1006  * Add review_status_id column to artifacts table.
1007  *
1008  * NOTE: For DBs created with schema 5 or 6 we define a foreign
1009  * key constraint on the review_status_column. We don't bother
1010  * with this for DBs updated to schema 5 or 6 because of
1011  * limitations of the SQLite ALTER TABLE command.
1012  */
1013  statement.execute("ALTER TABLE blackboard_artifacts ADD COLUMN review_status_id INTEGER NOT NULL DEFAULT " + BlackboardArtifact.ReviewStatus.UNDECIDED.getID());
1014  }
1015 
1016  return 6;
1017 
1018  } finally {
1019  closeResultSet(resultSet);
1020  closeStatement(statement);
1021  }
1022  }
1023 
1029  public int getSchemaVersion() {
1030  return this.versionNumber;
1031  }
1032 
1039  return this.dbType;
1040  }
1041 
1048  public String getBackupDatabasePath() {
1049  return dbBackupPath;
1050  }
1051 
1063  return new CaseDbTransaction(connections.getConnection());
1064  }
1065 
1071  public String getDatabaseName() {
1072  return databaseName;
1073  }
1074 
1081  public String getDbDirPath() {
1082  return caseDirPath;
1083  }
1084 
1090  public void acquireExclusiveLock() {
1091  if (dbType == DbType.SQLITE) {
1092  rwLock.writeLock().lock();
1093  }
1094  }
1095 
1101  public void releaseExclusiveLock() {
1102  if (dbType == DbType.SQLITE) {
1103  rwLock.writeLock().unlock();
1104  }
1105  }
1106 
1112  public void acquireSharedLock() {
1113  if (dbType == DbType.SQLITE) {
1114  rwLock.readLock().lock();
1115  }
1116  }
1117 
1123  public void releaseSharedLock() {
1124  if (dbType == DbType.SQLITE) {
1125  rwLock.readLock().unlock();
1126  }
1127  }
1128 
1138  public static SleuthkitCase openCase(String dbPath) throws TskCoreException {
1139  try {
1140  final SleuthkitJNI.CaseDbHandle caseHandle = SleuthkitJNI.openCaseDb(dbPath);
1141  return new SleuthkitCase(dbPath, caseHandle, DbType.SQLITE);
1142  } catch (Exception ex) {
1143  throw new TskCoreException("Failed to open case database at " + dbPath, ex);
1144  }
1145  }
1146 
1158  public static SleuthkitCase openCase(String databaseName, CaseDbConnectionInfo info, String caseDir) throws TskCoreException {
1159  try {
1160  /*
1161  * The flow of this method involves trying to open case and if
1162  * successful, return that case. If unsuccessful, an exception is
1163  * thrown. We catch any exceptions, and use tryConnect() to attempt
1164  * to obtain further information about the error. If tryConnect() is
1165  * unable to successfully connect, tryConnect() will throw a
1166  * TskCoreException with a message containing user-level error
1167  * reporting. If tryConnect() is able to connect, flow continues and
1168  * we rethrow the original exception obtained from trying to create
1169  * the case. In this way, we obtain more detailed information if we
1170  * are able, but do not lose any information if unable.
1171  */
1172  final SleuthkitJNI.CaseDbHandle caseHandle = SleuthkitJNI.openCaseDb(databaseName, info);
1173  return new SleuthkitCase(info.getHost(), Integer.parseInt(info.getPort()), databaseName, info.getUserName(), info.getPassword(), caseHandle, caseDir, info.getDbType());
1174  } catch (PropertyVetoException exp) {
1175  // In this case, the JDBC driver doesn't support PostgreSQL. Use the generic message here.
1176  throw new TskCoreException(exp.getMessage(), exp);
1177  } catch (Exception exp) {
1178  tryConnect(info); // attempt to connect, throw with user-friendly message if unable
1179  throw new TskCoreException(exp.getMessage(), exp); // throw with generic message if tryConnect() was successful
1180  }
1181  }
1182 
1192  public static SleuthkitCase newCase(String dbPath) throws TskCoreException {
1193  try {
1194  SleuthkitJNI.CaseDbHandle caseHandle = SleuthkitJNI.newCaseDb(dbPath);
1195  return new SleuthkitCase(dbPath, caseHandle, DbType.SQLITE);
1196  } catch (Exception ex) {
1197  throw new TskCoreException("Failed to create case database at " + dbPath, ex);
1198  }
1199  }
1200 
1216  public static SleuthkitCase newCase(String caseName, CaseDbConnectionInfo info, String caseDirPath) throws TskCoreException {
1217  String databaseName = createCaseDataBaseName(caseName);
1218  try {
1231  SleuthkitJNI.CaseDbHandle caseHandle = SleuthkitJNI.newCaseDb(databaseName, info);
1232  return new SleuthkitCase(info.getHost(), Integer.parseInt(info.getPort()),
1233  databaseName, info.getUserName(), info.getPassword(), caseHandle, caseDirPath, info.getDbType());
1234  } catch (PropertyVetoException exp) {
1235  // In this case, the JDBC driver doesn't support PostgreSQL. Use the generic message here.
1236  throw new TskCoreException(exp.getMessage(), exp);
1237  } catch (Exception exp) {
1238  tryConnect(info); // attempt to connect, throw with user-friendly message if unable
1239  throw new TskCoreException(exp.getMessage(), exp); // throw with generic message if tryConnect() was successful
1240  }
1241  }
1242 
1252  private static String createCaseDataBaseName(String candidateDbName) {
1253  String dbName;
1254  if (!candidateDbName.isEmpty()) {
1255  /*
1256  * Replace all non-ASCII characters.
1257  */
1258  dbName = candidateDbName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS
1259 
1260  /*
1261  * Replace all control characters.
1262  */
1263  dbName = dbName.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS
1264 
1265  /*
1266  * Replace /, \, :, ?, space, ' ".
1267  */
1268  dbName = dbName.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS
1269 
1270  /*
1271  * Make it all lowercase.
1272  */
1273  dbName = dbName.toLowerCase();
1274 
1275  /*
1276  * Must start with letter or underscore. If not, prepend an
1277  * underscore.
1278  */
1279  if ((dbName.length() > 0 && !(Character.isLetter(dbName.codePointAt(0))) && !(dbName.codePointAt(0) == '_'))) {
1280  dbName = "_" + dbName;
1281  }
1282 
1283  /*
1284  * Truncate to 63 - 16 = 47 chars to accomodate a timestamp for
1285  * uniqueness.
1286  */
1287  if (dbName.length() > MAX_DB_NAME_LEN_BEFORE_TIMESTAMP) {
1288  dbName = dbName.substring(0, MAX_DB_NAME_LEN_BEFORE_TIMESTAMP);
1289  }
1290 
1291  } else {
1292  /*
1293  * Must start with letter or underscore.
1294  */
1295  dbName = "_";
1296  }
1297  /*
1298  * Add the time stmap.
1299  */
1300  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
1301  Date date = new Date();
1302  dbName = dbName + "_" + dateFormat.format(date);
1303 
1304  return dbName;
1305  }
1306 
1322  public AddImageProcess makeAddImageProcess(String timezone, boolean addUnallocSpace, boolean noFatFsOrphans,
1323  String imageWriterPath) {
1324  return this.caseHandle.initAddImageProcess(timezone, addUnallocSpace, noFatFsOrphans, imageWriterPath);
1325  }
1326 
1335  public List<Content> getRootObjects() throws TskCoreException {
1336  CaseDbConnection connection = connections.getConnection();
1338  Statement s = null;
1339  ResultSet rs = null;
1340  try {
1341  s = connection.createStatement();
1342  rs = connection.executeQuery(s, "SELECT obj_id, type FROM tsk_objects " //NON-NLS
1343  + "WHERE par_obj_id IS NULL"); //NON-NLS
1344  Collection<ObjectInfo> infos = new ArrayList<ObjectInfo>();
1345  while (rs.next()) {
1346  infos.add(new ObjectInfo(rs.getLong("obj_id"), ObjectType.valueOf(rs.getShort("type")))); //NON-NLS
1347  }
1348 
1349  List<Content> rootObjs = new ArrayList<Content>();
1350  for (ObjectInfo i : infos) {
1351  if (null != i.type) {
1352  switch (i.type) {
1353  case IMG:
1354  rootObjs.add(getImageById(i.id));
1355  break;
1356  case ABSTRACTFILE:
1357  // Check if virtual dir for local files.
1358  AbstractFile af = getAbstractFileById(i.id);
1359  if (af instanceof VirtualDirectory) {
1360  rootObjs.add(af);
1361  } else {
1362  throw new TskCoreException("Parentless object has wrong type to be a root (ABSTRACTFILE, but not VIRTUAL_DIRECTORY: " + i.type);
1363  }
1364  break;
1365  default:
1366  throw new TskCoreException("Parentless object has wrong type to be a root: " + i.type);
1367  }
1368  }
1369  }
1370  return rootObjs;
1371  } catch (SQLException ex) {
1372  throw new TskCoreException("Error getting root objects", ex);
1373  } finally {
1374  closeResultSet(rs);
1375  closeStatement(s);
1376  connection.close();
1378  }
1379  }
1380 
1396  public List<DataSource> getDataSources() throws TskCoreException {
1397  CaseDbConnection connection = connections.getConnection();
1399  Statement s = null;
1400  ResultSet rs = null;
1401  try {
1402  s = connection.createStatement();
1403  rs = connection.executeQuery(s, "SELECT obj_id, device_id, time_zone FROM data_source_info"); //NON-NLS
1404  List<DataSource> dataSources = new ArrayList<DataSource>();
1405  while (rs.next()) {
1406  dataSources.add(new AbstractDataSource(rs.getLong("obj_id"), rs.getString("device_id"), rs.getString("time_zone")));
1407  }
1408  return dataSources;
1409  } catch (SQLException ex) {
1410  throw new TskCoreException("Error getting data sources", ex);
1411  } finally {
1412  closeResultSet(rs);
1413  closeStatement(s);
1414  connection.close();
1416  }
1417  }
1418 
1438  CaseDbConnection connection = connections.getConnection();
1440  Statement s = null;
1441  ResultSet rs = null;
1442  try {
1443  s = connection.createStatement();
1444  rs = connection.executeQuery(s, "SELECT device_id, time_zone FROM data_source_info WHERE obj_id = " + objectId); //NON-NLS
1445  if (rs.next()) {
1446  return new AbstractDataSource(objectId, rs.getString("device_id"), rs.getString("time_zone"));
1447  } else {
1448  throw new TskDataException(String.format("There is no data source with obj_id = %d", objectId));
1449  }
1450  } catch (SQLException ex) {
1451  throw new TskCoreException(String.format("Error getting data source with obj_id = %d", objectId), ex);
1452  } finally {
1453  closeResultSet(rs);
1454  closeStatement(s);
1455  connection.close();
1457  }
1458  }
1459 
1470  public ArrayList<BlackboardArtifact> getBlackboardArtifacts(int artifactTypeID) throws TskCoreException {
1471  return getArtifactsHelper("blackboard_artifacts.artifact_type_id = " + artifactTypeID);
1472  }
1473 
1484  public long getBlackboardArtifactsCount(long objId) throws TskCoreException {
1485  CaseDbConnection connection = connections.getConnection();
1487  ResultSet rs = null;
1488  try {
1489  // SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE obj_id = ?
1490  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_FROM_SOURCE);
1491  statement.clearParameters();
1492  statement.setLong(1, objId);
1493  rs = connection.executeQuery(statement);
1494  long count = 0;
1495  if (rs.next()) {
1496  count = rs.getLong("count");
1497  }
1498  return count;
1499  } catch (SQLException ex) {
1500  throw new TskCoreException("Error getting number of blackboard artifacts by content", ex);
1501  } finally {
1502  closeResultSet(rs);
1503  connection.close();
1505  }
1506  }
1507 
1518  public long getBlackboardArtifactsTypeCount(int artifactTypeID) throws TskCoreException {
1519  CaseDbConnection connection = connections.getConnection();
1521  ResultSet rs = null;
1522  try {
1523  // SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE artifact_type_id = ?
1524  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_OF_TYPE);
1525  statement.clearParameters();
1526  statement.setInt(1, artifactTypeID);
1527  rs = connection.executeQuery(statement);
1528  long count = 0;
1529  if (rs.next()) {
1530  count = rs.getLong("count");
1531  }
1532  return count;
1533  } catch (SQLException ex) {
1534  throw new TskCoreException("Error getting number of blackboard artifacts by type", ex);
1535  } finally {
1536  closeResultSet(rs);
1537  connection.close();
1539  }
1540  }
1541 
1556  public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, String value) throws TskCoreException {
1557  CaseDbConnection connection = connections.getConnection();
1559  Statement s = null;
1560  ResultSet rs = null;
1561  try {
1562  s = connection.createStatement();
1563  rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS
1564  + "arts.obj_id AS obj_id, arts.artifact_type_id AS artifact_type_id, "
1565  + "types.type_name AS type_name, types.display_name AS display_name, "//NON-NLS
1566  + " arts.review_status_id AS review_status_id " //NON-NLS
1567  + "FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS
1568  + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS
1569  + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS
1570  + " AND attrs.value_text = '" + value + "'"
1571  + " AND types.artifact_type_id=arts.artifact_type_id"
1572  + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); //NON-NLS
1573  ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>();
1574  while (rs.next()) {
1575  artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"),
1576  rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"),
1577  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))));
1578  }
1579  return artifacts;
1580  } catch (SQLException ex) {
1581  throw new TskCoreException("Error getting blackboard artifacts by attribute", ex);
1582  } finally {
1583  closeResultSet(rs);
1584  closeStatement(s);
1585  connection.close();
1587  }
1588  }
1589 
1607  public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, String subString, boolean startsWith) throws TskCoreException {
1608  String valSubStr = "%" + subString; //NON-NLS
1609  if (startsWith == false) {
1610  valSubStr += "%"; //NON-NLS
1611  }
1612  CaseDbConnection connection = connections.getConnection();
1614  Statement s = null;
1615  ResultSet rs = null;
1616  try {
1617  s = connection.createStatement();
1618  rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS
1619  + " arts.obj_id AS obj_id, arts.artifact_type_id AS artifact_type_id, " //NON-NLS
1620  + " types.type_name AS type_name, types.display_name AS display_name, " //NON-NLS
1621  + " arts.review_status_id AS review_status_id " //NON-NLS
1622  + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS
1623  + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS
1624  + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS
1625  + " AND LOWER(attrs.value_text) LIKE LOWER('" + valSubStr + "')"
1626  + " AND types.artifact_type_id=arts.artifact_type_id "
1627  + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());
1628  ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>();
1629  while (rs.next()) {
1630  artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"),
1631  rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"),
1632  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))));
1633  }
1634  return artifacts;
1635  } catch (SQLException ex) {
1636  throw new TskCoreException("Error getting blackboard artifacts by attribute. " + ex.getMessage(), ex);
1637  } finally {
1638  closeResultSet(rs);
1639  closeStatement(s);
1640  connection.close();
1642  }
1643  }
1644 
1659  public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, int value) throws TskCoreException {
1660  CaseDbConnection connection = connections.getConnection();
1662  Statement s = null;
1663  ResultSet rs = null;
1664  try {
1665  s = connection.createStatement();
1666  rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS
1667  + " arts.obj_id AS obj_id, arts.artifact_type_id AS artifact_type_id, "
1668  + " types.type_name AS type_name, types.display_name AS display_name, "
1669  + " arts.review_status_id AS review_status_id "//NON-NLS
1670  + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS
1671  + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS
1672  + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS
1673  + " AND attrs.value_int32 = " + value //NON-NLS
1674  + " AND types.artifact_type_id=arts.artifact_type_id "
1675  + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());
1676  ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>();
1677  while (rs.next()) {
1678  artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"),
1679  rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"),
1680  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))));
1681  }
1682  return artifacts;
1683  } catch (SQLException ex) {
1684  throw new TskCoreException("Error getting blackboard artifacts by attribute", ex);
1685  } finally {
1686  closeResultSet(rs);
1687  closeStatement(s);
1688  connection.close();
1690  }
1691  }
1692 
1707  public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, long value) throws TskCoreException {
1708  CaseDbConnection connection = connections.getConnection();
1710  Statement s = null;
1711  ResultSet rs = null;
1712  try {
1713  s = connection.createStatement();
1714  rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS
1715  + " arts.obj_id AS obj_id, arts.artifact_type_id AS artifact_type_id, "
1716  + " types.type_name AS type_name, types.display_name AS display_name, "
1717  + " arts.review_status_id AS review_status_id "//NON-NLS
1718  + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS
1719  + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS
1720  + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS
1721  + " AND attrs.value_int64 = " + value //NON-NLS
1722  + " AND types.artifact_type_id=arts.artifact_type_id "
1723  + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());
1724  ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>();
1725  while (rs.next()) {
1726  artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"),
1727  rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"),
1728  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))));
1729  }
1730  return artifacts;
1731  } catch (SQLException ex) {
1732  throw new TskCoreException("Error getting blackboard artifacts by attribute. " + ex.getMessage(), ex);
1733  } finally {
1734  closeResultSet(rs);
1735  closeStatement(s);
1736  connection.close();
1738  }
1739  }
1740 
1755  public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, double value) throws TskCoreException {
1756  CaseDbConnection connection = connections.getConnection();
1758  Statement s = null;
1759  ResultSet rs = null;
1760  try {
1761  s = connection.createStatement();
1762  rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS
1763  + " arts.obj_id AS obj_id, arts.artifact_type_id AS artifact_type_id, "
1764  + " types.type_name AS type_name, types.display_name AS display_name, "
1765  + " arts.review_status_id AS review_status_id "//NON-NLS
1766  + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS
1767  + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS
1768  + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS
1769  + " AND attrs.value_double = " + value //NON-NLS
1770  + " AND types.artifact_type_id=arts.artifact_type_id "
1771  + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());
1772  ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>();
1773  while (rs.next()) {
1774  artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"),
1775  rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"),
1776  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))));
1777  }
1778  return artifacts;
1779  } catch (SQLException ex) {
1780  throw new TskCoreException("Error getting blackboard artifacts by attribute", ex);
1781  } finally {
1782  closeResultSet(rs);
1783  closeStatement(s);
1784  connection.close();
1786  }
1787  }
1788 
1803  public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, byte value) throws TskCoreException {
1804  CaseDbConnection connection = connections.getConnection();
1806  Statement s = null;
1807  ResultSet rs = null;
1808  try {
1809  s = connection.createStatement();
1810  rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS
1811  + " arts.obj_id AS obj_id, arts.artifact_type_id AS artifact_type_id, "
1812  + " types.type_name AS type_name, types.display_name AS display_name, "
1813  + " arts.review_status_id AS review_status_id "//NON-NLS
1814  + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS
1815  + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS
1816  + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS
1817  + " AND attrs.value_byte = " + value //NON-NLS
1818  + " AND types.artifact_type_id=arts.artifact_type_id "
1819  + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());
1820  ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>();
1821  while (rs.next()) {
1822  artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"),
1823  rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"),
1824  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))));
1825  }
1826  return artifacts;
1827  } catch (SQLException ex) {
1828  throw new TskCoreException("Error getting blackboard artifacts by attribute", ex);
1829  } finally {
1830  closeResultSet(rs);
1831  closeStatement(s);
1832  connection.close();
1834  }
1835  }
1836 
1845  CaseDbConnection connection = connections.getConnection();
1847  Statement s = null;
1848  ResultSet rs = null;
1849  try {
1850  s = connection.createStatement();
1851  rs = connection.executeQuery(s, "SELECT artifact_type_id, type_name, display_name FROM blackboard_artifact_types"); //NON-NLS
1852  ArrayList<BlackboardArtifact.Type> artifactTypes = new ArrayList<BlackboardArtifact.Type>();
1853  while (rs.next()) {
1854  artifactTypes.add(new BlackboardArtifact.Type(rs.getInt("artifact_type_id"),
1855  rs.getString("type_name"), rs.getString("display_name")));
1856  }
1857  return artifactTypes;
1858  } catch (SQLException ex) {
1859  throw new TskCoreException("Error getting artifact types", ex); //NON-NLS
1860  } finally {
1861  closeResultSet(rs);
1862  closeStatement(s);
1863  connection.close();
1865  }
1866  }
1867 
1877  String typeIdList = "";
1878  for (int i = 0; i < BlackboardArtifact.ARTIFACT_TYPE.values().length; ++i) {
1879  typeIdList += BlackboardArtifact.ARTIFACT_TYPE.values()[i].getTypeID();
1880  if (i < BlackboardArtifact.ARTIFACT_TYPE.values().length - 1) {
1881  typeIdList += ", ";
1882  }
1883  }
1884  String query = "SELECT DISTINCT artifact_type_id FROM blackboard_artifacts "
1885  + "WHERE artifact_type_id IN (" + typeIdList + ")";
1886  CaseDbConnection connection = connections.getConnection();
1888  Statement s = null;
1889  ResultSet rs = null;
1890  try {
1891  s = connection.createStatement();
1892  rs = connection.executeQuery(s, query);
1893  ArrayList<BlackboardArtifact.ARTIFACT_TYPE> usedArts = new ArrayList<BlackboardArtifact.ARTIFACT_TYPE>();
1894  while (rs.next()) {
1895  usedArts.add(ARTIFACT_TYPE.fromID(rs.getInt("artifact_type_id")));
1896  }
1897  return usedArts;
1898  } catch (SQLException ex) {
1899  throw new TskCoreException("Error getting artifact types in use", ex);
1900  } finally {
1901  closeResultSet(rs);
1902  closeStatement(s);
1903  connection.close();
1905  }
1906  }
1907 
1919  CaseDbConnection connection = connections.getConnection();
1921  Statement s = null;
1922  ResultSet rs = null;
1923  try {
1924  s = connection.createStatement();
1925  rs = connection.executeQuery(s,
1926  "SELECT DISTINCT arts.artifact_type_id AS artifact_type_id, "
1927  + "types.type_name AS type_name, types.display_name AS display_name "
1928  + "FROM blackboard_artifact_types AS types "
1929  + "INNER JOIN blackboard_artifacts AS arts "
1930  + "ON arts.artifact_type_id = types.artifact_type_id"); //NON-NLS
1931  List<BlackboardArtifact.Type> uniqueArtifactTypes = new ArrayList<BlackboardArtifact.Type>();
1932  while (rs.next()) {
1933  uniqueArtifactTypes.add(new BlackboardArtifact.Type(rs.getInt("artifact_type_id"),
1934  rs.getString("type_name"), rs.getString("display_name")));
1935  }
1936  return uniqueArtifactTypes;
1937  } catch (SQLException ex) {
1938  throw new TskCoreException("Error getting attribute types", ex);
1939  } finally {
1940  closeResultSet(rs);
1941  closeStatement(s);
1942  connection.close();
1944  }
1945  }
1946 
1955  CaseDbConnection connection = connections.getConnection();
1957  Statement s = null;
1958  ResultSet rs = null;
1959  try {
1960  s = connection.createStatement();
1961  rs = connection.executeQuery(s, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types"); //NON-NLS
1962  ArrayList<BlackboardAttribute.Type> attribute_types = new ArrayList<BlackboardAttribute.Type>();
1963  while (rs.next()) {
1964  attribute_types.add(new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"),
1965  rs.getString("display_name"), TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type"))));
1966  }
1967  return attribute_types;
1968  } catch (SQLException ex) {
1969  throw new TskCoreException("Error getting attribute types", ex);
1970  } finally {
1971  closeResultSet(rs);
1972  closeStatement(s);
1973  connection.close();
1975  }
1976  }
1977 
1990  CaseDbConnection connection = connections.getConnection();
1992  Statement s = null;
1993  ResultSet rs = null;
1994  try {
1995  s = connection.createStatement();
1996  rs = connection.executeQuery(s, "SELECT COUNT(*) AS count FROM blackboard_attribute_types"); //NON-NLS
1997  int count = 0;
1998  if (rs.next()) {
1999  count = rs.getInt("count");
2000  }
2001  return count;
2002  } catch (SQLException ex) {
2003  throw new TskCoreException("Error getting number of blackboard artifacts by type", ex);
2004  } finally {
2005  closeResultSet(rs);
2006  closeStatement(s);
2007  connection.close();
2009  }
2010  }
2011 
2024  private ArrayList<BlackboardArtifact> getArtifactsHelper(String whereClause) throws TskCoreException {
2025  CaseDbConnection connection = connections.getConnection();
2027  ResultSet rs = null;
2028  try {
2029  Statement statement = connection.createStatement();
2030  String query = "SELECT blackboard_artifacts.artifact_id AS artifact_id, "
2031  + "blackboard_artifacts.obj_id AS obj_id, "
2032  + "blackboard_artifact_types.artifact_type_id AS artifact_type_id, "
2033  + "blackboard_artifact_types.type_name AS type_name, "
2034  + "blackboard_artifact_types.display_name AS display_name, "
2035  + "blackboard_artifacts.review_status_id AS review_status_id "
2036  + "FROM blackboard_artifacts, blackboard_artifact_types "
2037  + "WHERE blackboard_artifacts.artifact_type_id = blackboard_artifact_types.artifact_type_id "
2038  + " AND blackboard_artifacts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()
2039  + " AND " + whereClause;
2040  rs = connection.executeQuery(statement, query);
2041  ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>();
2042  while (rs.next()) {
2043  artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"),
2044  rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"),
2045  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))));
2046  }
2047  return artifacts;
2048  } catch (SQLException ex) {
2049  throw new TskCoreException("Error getting or creating a blackboard artifact", ex);
2050  } finally {
2051  closeResultSet(rs);
2052  connection.close();
2054  }
2055  }
2056 
2069  private long getArtifactsCountHelper(int artifactTypeID, long obj_id) throws TskCoreException {
2070  CaseDbConnection connection = connections.getConnection();
2072  ResultSet rs = null;
2073  try {
2074  // SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE obj_id = ? AND artifact_type_id = ?
2075  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_BY_SOURCE_AND_TYPE);
2076  statement.clearParameters();
2077  statement.setLong(1, obj_id);
2078  statement.setInt(2, artifactTypeID);
2079  rs = connection.executeQuery(statement);
2080  long count = 0;
2081  if (rs.next()) {
2082  count = rs.getLong("count");
2083  }
2084  return count;
2085  } catch (SQLException ex) {
2086  throw new TskCoreException("Error getting blackboard artifact count", ex);
2087  } finally {
2088  closeResultSet(rs);
2089  connection.close();
2091  }
2092  }
2093 
2106  public ArrayList<BlackboardArtifact> getBlackboardArtifacts(String artifactTypeName, long obj_id) throws TskCoreException {
2107  return getArtifactsHelper("blackboard_artifacts.obj_id = " + obj_id + " AND blackboard_artifact_types.type_name = '" + artifactTypeName + "';");
2108  }
2109 
2122  public ArrayList<BlackboardArtifact> getBlackboardArtifacts(int artifactTypeID, long obj_id) throws TskCoreException {
2123  return getArtifactsHelper("blackboard_artifacts.obj_id = " + obj_id + " AND blackboard_artifact_types.artifact_type_id = " + artifactTypeID + ";");
2124  }
2125 
2138  public ArrayList<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactType, long obj_id) throws TskCoreException {
2139  return getBlackboardArtifacts(artifactType.getTypeID(), obj_id);
2140  }
2141 
2154  public long getBlackboardArtifactsCount(String artifactTypeName, long obj_id) throws TskCoreException {
2155  int artifactTypeID = this.getArtifactType(artifactTypeName).getTypeID();
2156  if (artifactTypeID == -1) {
2157  return 0;
2158  }
2159  return getArtifactsCountHelper(artifactTypeID, obj_id);
2160  }
2161 
2174  public long getBlackboardArtifactsCount(int artifactTypeID, long obj_id) throws TskCoreException {
2175  return getArtifactsCountHelper(artifactTypeID, obj_id);
2176  }
2177 
2190  public long getBlackboardArtifactsCount(ARTIFACT_TYPE artifactType, long obj_id) throws TskCoreException {
2191  return getArtifactsCountHelper(artifactType.getTypeID(), obj_id);
2192  }
2193 
2205  public ArrayList<BlackboardArtifact> getBlackboardArtifacts(String artifactTypeName) throws TskCoreException {
2206  return getArtifactsHelper("blackboard_artifact_types.type_name = '" + artifactTypeName + "';");
2207  }
2208 
2220  public ArrayList<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactType) throws TskCoreException {
2221  return getArtifactsHelper("blackboard_artifact_types.artifact_type_id = " + artifactType.getTypeID() + ";");
2222  }
2223 
2237  public List<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactType, BlackboardAttribute.ATTRIBUTE_TYPE attrType, String value) throws TskCoreException {
2238  CaseDbConnection connection = connections.getConnection();
2240  Statement s = null;
2241  ResultSet rs = null;
2242  try {
2243  s = connection.createStatement();
2244  rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS
2245  + "arts.obj_id AS obj_id, arts.artifact_type_id AS artifact_type_id, "
2246  + "types.type_name AS type_name, types.display_name AS display_name,"
2247  + "arts.review_status_id AS review_status_id "//NON-NLS
2248  + "FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS
2249  + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS
2250  + "AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS
2251  + " AND arts.artifact_type_id = " + artifactType.getTypeID() //NON-NLS
2252  + " AND attrs.value_text = '" + value + "'" //NON-NLS
2253  + " AND types.artifact_type_id=arts.artifact_type_id"
2254  + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());
2255  ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>();
2256  while (rs.next()) {
2257  artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"),
2258  rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"),
2259  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))));
2260  }
2261  return artifacts;
2262  } catch (SQLException ex) {
2263  throw new TskCoreException("Error getting blackboard artifacts by artifact type and attribute. " + ex.getMessage(), ex);
2264  } finally {
2265  closeResultSet(rs);
2266  closeStatement(s);
2267  connection.close();
2269  }
2270  }
2271 
2283  CaseDbConnection connection = connections.getConnection();
2285  ResultSet rs = null;
2286  Statement s = null;
2287  try {
2288  s = connection.createStatement();
2289  rs = connection.executeQuery(s, "SELECT arts.artifact_id AS artifact_id, "
2290  + "arts.obj_id AS obj_id, arts.artifact_type_id AS artifact_type_id, "
2291  + "types.type_name AS type_name, types.display_name AS display_name,"
2292  + "arts.review_status_id AS review_status_id "//NON-NLS
2293  + "FROM blackboard_artifacts AS arts, blackboard_artifact_types AS types "
2294  + "WHERE arts.artifact_id = " + artifactID
2295  + " AND arts.artifact_type_id = types.artifact_type_id");
2296  if (rs.next()) {
2297  return new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"),
2298  rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"),
2299  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")));
2300  } else {
2301  /*
2302  * I think this should actually return null (or Optional) when
2303  * there is no artifact with the given id, but it looks like
2304  * existing code is not expecting that. -jm
2305  */
2306  throw new TskCoreException("No blackboard artifact with id " + artifactID);
2307  }
2308  } catch (SQLException ex) {
2309  throw new TskCoreException("Error getting a blackboard artifact. " + ex.getMessage(), ex);
2310  } finally {
2311  closeResultSet(rs);
2312  connection.close();
2314  }
2315  }
2316 
2325  public void addBlackboardAttribute(BlackboardAttribute attr, int artifactTypeId) throws TskCoreException {
2326  CaseDbConnection connection = connections.getConnection();
2328  try {
2329  addBlackBoardAttribute(attr, artifactTypeId, connection);
2330  } catch (SQLException ex) {
2331  throw new TskCoreException("Error adding blackboard attribute " + attr.toString(), ex);
2332  } finally {
2333  connection.close();
2335  }
2336  }
2337 
2347  public void addBlackboardAttributes(Collection<BlackboardAttribute> attributes, int artifactTypeId) throws TskCoreException {
2348  CaseDbConnection connection = connections.getConnection();
2350  try {
2351  connection.beginTransaction();
2352  for (final BlackboardAttribute attr : attributes) {
2353  addBlackBoardAttribute(attr, artifactTypeId, connection);
2354  }
2355  connection.commitTransaction();
2356  } catch (SQLException ex) {
2357  connection.rollbackTransaction();
2358  throw new TskCoreException("Error adding blackboard attributes", ex);
2359  } finally {
2360  connection.close();
2362  }
2363  }
2364 
2365  private void addBlackBoardAttribute(BlackboardAttribute attr, int artifactTypeId, CaseDbConnection connection) throws SQLException, TskCoreException {
2366  PreparedStatement statement;
2367  switch (attr.getAttributeType().getValueType()) {
2368  case STRING:
2369  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_STRING_ATTRIBUTE);
2370  statement.clearParameters();
2371  statement.setString(7, attr.getValueString());
2372  break;
2373  case BYTE:
2374  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_BYTE_ATTRIBUTE);
2375  statement.clearParameters();
2376  statement.setBytes(7, attr.getValueBytes());
2377  break;
2378  case INTEGER:
2379  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_INT_ATTRIBUTE);
2380  statement.clearParameters();
2381  statement.setInt(7, attr.getValueInt());
2382  break;
2383  case LONG:
2384  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LONG_ATTRIBUTE);
2385  statement.clearParameters();
2386  statement.setLong(7, attr.getValueLong());
2387  break;
2388  case DOUBLE:
2389  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_DOUBLE_ATTRIBUTE);
2390  statement.clearParameters();
2391  statement.setDouble(7, attr.getValueDouble());
2392  break;
2393  case DATETIME:
2394  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LONG_ATTRIBUTE);
2395  statement.clearParameters();
2396  statement.setLong(7, attr.getValueLong());
2397  break;
2398  default:
2399  throw new TskCoreException("Unrecognized artifact attribute value type");
2400  }
2401  statement.setLong(1, attr.getArtifactID());
2402  statement.setInt(2, artifactTypeId);
2403  statement.setString(3, attr.getSourcesCSV());
2404  statement.setString(4, "");
2405  statement.setInt(5, attr.getAttributeType().getTypeID());
2406  statement.setLong(6, attr.getAttributeType().getValueType().getType());
2407  connection.executeUpdate(statement);
2408  }
2409 
2420  String addSourceToArtifactAttribute(BlackboardAttribute attr, String source) throws TskCoreException {
2421  /*
2422  * WARNING: This is a temporary implementation that is not safe and
2423  * denormalizes the case datbase.
2424  *
2425  * TODO (JIRA-2294): Provide a safe and normalized solution to tracking
2426  * the sources of artifact attributes.
2427  */
2428  if (null == source || source.isEmpty()) {
2429  throw new TskCoreException("Attempt to add null or empty source module name to artifact attribute");
2430  }
2431  CaseDbConnection connection = connections.getConnection();
2433  Statement queryStmt = null;
2434  Statement updateStmt = null;
2435  ResultSet result = null;
2436  String newSources = "";
2437  try {
2438  connection.beginTransaction();
2439  String valueClause = "";
2440  BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType = attr.getAttributeType().getValueType();
2441  if (BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE != valueType) {
2442  switch (valueType) {
2443  case STRING:
2444  valueClause = " value_text = '" + escapeSingleQuotes(attr.getValueString()) + "'";
2445  break;
2446  case INTEGER:
2447  valueClause = " value_int32 = " + attr.getValueInt();
2448  break;
2449  case LONG:
2450  case DATETIME:
2451  valueClause = " value_int64 = " + attr.getValueLong();
2452  break;
2453  case DOUBLE:
2454  valueClause = " value_double = " + attr.getValueDouble();
2455  break;
2456  default:
2457  throw new TskCoreException(String.format("Unrecognized value type for attribute %s", attr.getDisplayString()));
2458  }
2459  String query = "SELECT source FROM blackboard_attributes WHERE"
2460  + " artifact_id = " + attr.getArtifactID()
2461  + " AND attribute_type_id = " + attr.getAttributeType().getTypeID()
2462  + " AND value_type = " + attr.getAttributeType().getValueType().getType()
2463  + " AND " + valueClause + ";";
2464  queryStmt = connection.createStatement();
2465  updateStmt = connection.createStatement();
2466  result = connection.executeQuery(queryStmt, query);
2467  } else {
2468  /*
2469  * SELECT source FROM blackboard_attributes WHERE artifact_id =
2470  * ? AND attribute_type_id = ? AND value_type = 4 AND value_byte
2471  * = ?
2472  */
2473  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ATTR_BY_VALUE_BYTE);
2474  statement.clearParameters();
2475  statement.setLong(1, attr.getArtifactID());
2476  statement.setLong(2, attr.getAttributeType().getTypeID());
2477  statement.setBytes(3, attr.getValueBytes());
2478  result = connection.executeQuery(statement);
2479  }
2480  while (result.next()) {
2481  String oldSources = result.getString("source");
2482  if (null != oldSources && !oldSources.isEmpty()) {
2483  Set<String> uniqueSources = new HashSet<String>(Arrays.asList(oldSources.split(",")));
2484  if (!uniqueSources.contains(source)) {
2485  newSources = oldSources + "," + source;
2486  } else {
2487  newSources = oldSources;
2488  }
2489  } else {
2490  newSources = source;
2491  }
2492  if (BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE != valueType) {
2493  String update = "UPDATE blackboard_attributes SET source = '" + newSources + "' WHERE"
2494  + " artifact_id = " + attr.getArtifactID()
2495  + " AND attribute_type_id = " + attr.getAttributeType().getTypeID()
2496  + " AND value_type = " + attr.getAttributeType().getValueType().getType()
2497  + " AND " + valueClause + ";";
2498  connection.executeUpdate(updateStmt, update);
2499  } else {
2500  /*
2501  * UPDATE blackboard_attributes SET source = ? WHERE
2502  * artifact_id = ? AND attribute_type_id = ? AND value_type
2503  * = 4 AND value_byte = ?
2504  */
2505  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.UPDATE_ATTR_BY_VALUE_BYTE);
2506  statement.clearParameters();
2507  statement.setString(1, newSources);
2508  statement.setLong(2, attr.getArtifactID());
2509  statement.setLong(3, attr.getAttributeType().getTypeID());
2510  statement.setBytes(4, attr.getValueBytes());
2511  connection.executeUpdate(statement);
2512  }
2513  }
2514  connection.commitTransaction();
2515  return newSources;
2516  } catch (SQLException ex) {
2517  connection.rollbackTransaction();
2518  throw new TskCoreException(String.format("Error adding source module to attribute %s", attr.getDisplayString()), ex);
2519  } finally {
2520  closeResultSet(result);
2521  closeStatement(updateStmt);
2522  closeStatement(queryStmt);
2523  connection.close();
2525  }
2526  }
2527 
2542  public BlackboardAttribute.Type addArtifactAttributeType(String attrTypeString, TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName) throws TskCoreException, TskDataException {
2543  CaseDbConnection connection = connections.getConnection();
2545  Statement s = null;
2546  ResultSet rs = null;
2547  try {
2548  connection.beginTransaction();
2549  s = connection.createStatement();
2550  rs = connection.executeQuery(s, "SELECT attribute_type_id FROM blackboard_attribute_types WHERE type_name = '" + attrTypeString + "'"); //NON-NLS
2551  if (!rs.next()) {
2552  rs.close();
2553  rs = connection.executeQuery(s, "SELECT MAX(attribute_type_id) AS highest_id FROM blackboard_attribute_types");
2554  int maxID = 0;
2555  if (rs.next()) {
2556  maxID = rs.getInt("highest_id");
2557  if (maxID < MIN_USER_DEFINED_TYPE_ID) {
2558  maxID = MIN_USER_DEFINED_TYPE_ID;
2559  } else {
2560  maxID++;
2561  }
2562  }
2563  connection.executeUpdate(s, "INSERT INTO blackboard_attribute_types (attribute_type_id, type_name, display_name, value_type) VALUES ('" + maxID + "', '" + attrTypeString + "', '" + displayName + "', '" + valueType.getType() + "')"); //NON-NLS
2564  BlackboardAttribute.Type type = new BlackboardAttribute.Type(maxID, attrTypeString, displayName, valueType);
2565  this.typeIdToAttributeTypeMap.put(type.getTypeID(), type);
2566  this.typeNameToAttributeTypeMap.put(type.getTypeName(), type);
2567  connection.commitTransaction();
2568  return type;
2569  } else {
2570  throw new TskDataException("The attribute type that was added was already within the system.");
2571  }
2572 
2573  } catch (SQLException ex) {
2574  connection.rollbackTransaction();
2575  throw new TskCoreException("Error adding attribute type", ex);
2576  } finally {
2577  closeResultSet(rs);
2578  closeStatement(s);
2579  connection.close();
2581  }
2582  }
2583 
2594  public BlackboardAttribute.Type getAttributeType(String attrTypeName) throws TskCoreException {
2595  if (this.typeNameToAttributeTypeMap.containsKey(attrTypeName)) {
2596  return this.typeNameToAttributeTypeMap.get(attrTypeName);
2597  }
2598  CaseDbConnection connection = connections.getConnection();
2600  Statement s = null;
2601  ResultSet rs = null;
2602  try {
2603  s = connection.createStatement();
2604  rs = connection.executeQuery(s, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types WHERE type_name = '" + attrTypeName + "'"); //NON-NLS
2605  BlackboardAttribute.Type type = null;
2606  if (rs.next()) {
2607  type = new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"),
2608  rs.getString("display_name"), TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type")));
2609  this.typeIdToAttributeTypeMap.put(type.getTypeID(), type);
2610  this.typeNameToAttributeTypeMap.put(attrTypeName, type);
2611  }
2612  return type;
2613  } catch (SQLException ex) {
2614  throw new TskCoreException("Error getting attribute type id", ex);
2615  } finally {
2616  closeResultSet(rs);
2617  closeStatement(s);
2618  connection.close();
2620  }
2621  }
2622 
2634  if (this.typeIdToAttributeTypeMap.containsKey(typeID)) {
2635  return this.typeIdToAttributeTypeMap.get(typeID);
2636  }
2637  CaseDbConnection connection = connections.getConnection();
2639  Statement s = null;
2640  ResultSet rs = null;
2641  try {
2642  s = connection.createStatement();
2643  rs = connection.executeQuery(s, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types WHERE attribute_type_id = " + typeID + ""); //NON-NLS
2644  BlackboardAttribute.Type type = null;
2645  if (rs.next()) {
2646  type = new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"),
2647  rs.getString("display_name"), TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type")));
2648  this.typeIdToAttributeTypeMap.put(typeID, type);
2649  this.typeNameToAttributeTypeMap.put(type.getTypeName(), type);
2650  }
2651  return type;
2652  } catch (SQLException ex) {
2653  throw new TskCoreException("Error getting attribute type id", ex);
2654  } finally {
2655  closeResultSet(rs);
2656  closeStatement(s);
2657  connection.close();
2659  }
2660  }
2661 
2672  public BlackboardArtifact.Type getArtifactType(String artTypeName) throws TskCoreException {
2673  if (this.typeNameToArtifactTypeMap.containsKey(artTypeName)) {
2674  return this.typeNameToArtifactTypeMap.get(artTypeName);
2675  }
2676  CaseDbConnection connection = connections.getConnection();
2678  Statement s = null;
2679  ResultSet rs = null;
2680  try {
2681  s = connection.createStatement();
2682  rs = connection.executeQuery(s, "SELECT artifact_type_id, type_name, display_name FROM blackboard_artifact_types WHERE type_name = '" + artTypeName + "'"); //NON-NLS
2683  BlackboardArtifact.Type type = null;
2684  if (rs.next()) {
2685  type = new BlackboardArtifact.Type(rs.getInt("artifact_type_id"),
2686  rs.getString("type_name"), rs.getString("display_name"));
2687  this.typeIdToArtifactTypeMap.put(type.getTypeID(), type);
2688  this.typeNameToArtifactTypeMap.put(artTypeName, type);
2689  }
2690  return type;
2691  } catch (SQLException ex) {
2692  throw new TskCoreException("Error getting artifact type from the database", ex);
2693  } finally {
2694  closeResultSet(rs);
2695  closeStatement(s);
2696  connection.close();
2698  }
2699  }
2700 
2712  if (this.typeIdToArtifactTypeMap.containsKey(artTypeId)) {
2713  return typeIdToArtifactTypeMap.get(artTypeId);
2714  }
2715  CaseDbConnection connection = connections.getConnection();
2717  Statement s = null;
2718  ResultSet rs = null;
2719  try {
2720  s = connection.createStatement();
2721  rs = connection.executeQuery(s, "SELECT artifact_type_id, type_name, display_name FROM blackboard_artifact_types WHERE artifact_type_id = " + artTypeId + ""); //NON-NLS
2722  BlackboardArtifact.Type type = null;
2723  if (rs.next()) {
2724  type = new BlackboardArtifact.Type(rs.getInt("artifact_type_id"),
2725  rs.getString("type_name"), rs.getString("display_name"));
2726  this.typeIdToArtifactTypeMap.put(artTypeId, type);
2727  this.typeNameToArtifactTypeMap.put(type.getTypeName(), type);
2728  }
2729  return type;
2730  } catch (SQLException ex) {
2731  throw new TskCoreException("Error getting artifact type from the database", ex);
2732  } finally {
2733  closeResultSet(rs);
2734  closeStatement(s);
2735  connection.close();
2737  }
2738  }
2739 
2752  public BlackboardArtifact.Type addBlackboardArtifactType(String artifactTypeName, String displayName) throws TskCoreException, TskDataException {
2753  CaseDbConnection connection = connections.getConnection();
2755  Statement s = null;
2756  ResultSet rs = null;
2757  try {
2758  connection.beginTransaction();
2759  s = connection.createStatement();
2760  rs = connection.executeQuery(s, "SELECT artifact_type_id FROM blackboard_artifact_types WHERE type_name = '" + artifactTypeName + "'"); //NON-NLS
2761  if (!rs.next()) {
2762  rs.close();
2763  rs = connection.executeQuery(s, "SELECT MAX(artifact_type_id) AS highest_id FROM blackboard_artifact_types");
2764  int maxID = 0;
2765  if (rs.next()) {
2766  maxID = rs.getInt("highest_id");
2767  if (maxID < MIN_USER_DEFINED_TYPE_ID) {
2768  maxID = MIN_USER_DEFINED_TYPE_ID;
2769  } else {
2770  maxID++;
2771  }
2772  }
2773  connection.executeUpdate(s, "INSERT INTO blackboard_artifact_types (artifact_type_id, type_name, display_name) VALUES ('" + maxID + "', '" + artifactTypeName + "', '" + displayName + "')"); //NON-NLS
2774  BlackboardArtifact.Type type = new BlackboardArtifact.Type(maxID, artifactTypeName, displayName);
2775  this.typeIdToArtifactTypeMap.put(type.getTypeID(), type);
2776  this.typeNameToArtifactTypeMap.put(type.getTypeName(), type);
2777  connection.commitTransaction();
2778  return type;
2779  } else {
2780  throw new TskDataException("The attribute type that was added was already within the system.");
2781  }
2782  } catch (SQLException ex) {
2783  connection.rollbackTransaction();
2784  throw new TskCoreException("Error adding artifact type", ex);
2785  } finally {
2786  closeResultSet(rs);
2787  closeStatement(s);
2788  connection.close();
2790  }
2791  }
2792 
2793  public ArrayList<BlackboardAttribute> getBlackboardAttributes(final BlackboardArtifact artifact) throws TskCoreException {
2794  CaseDbConnection connection = connections.getConnection();
2796  ResultSet rs = null;
2797  try {
2798  Statement statement = connection.createStatement();
2799  rs = connection.executeQuery(statement, "SELECT attrs.artifact_id AS artifact_id, "
2800  + "attrs.source AS source, attrs.context AS context, attrs.attribute_type_id AS attribute_type_id, "
2801  + "attrs.value_type AS value_type, attrs.value_byte AS value_byte, "
2802  + "attrs.value_text AS value_text, attrs.value_int32 AS value_int32, "
2803  + "attrs.value_int64 AS value_int64, attrs.value_double AS value_double, "
2804  + "types.type_name AS type_name, types.display_name AS display_name "
2805  + "FROM blackboard_attributes AS attrs, blackboard_attribute_types AS types WHERE attrs.artifact_id = " + artifact.getArtifactID()
2806  + " AND attrs.attribute_type_id = types.attribute_type_id");
2807  ArrayList<BlackboardAttribute> attributes = new ArrayList<BlackboardAttribute>();
2808  while (rs.next()) {
2809  final BlackboardAttribute attr = new BlackboardAttribute(
2810  rs.getLong("artifact_id"),
2811  new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"),
2812  rs.getString("display_name"), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getInt("value_type"))),
2813  rs.getString("source"),
2814  rs.getString("context"),
2815  rs.getInt("value_int32"),
2816  rs.getLong("value_int64"),
2817  rs.getDouble("value_double"),
2818  rs.getString("value_text"),
2819  rs.getBytes("value_byte"), this
2820  );
2821  attributes.add(attr);
2822  }
2823  return attributes;
2824  } catch (SQLException ex) {
2825  throw new TskCoreException("Error getting attributes for artifact, artifact id = " + artifact.getArtifactID(), ex);
2826  } finally {
2827  closeResultSet(rs);
2828  connection.close();
2830  }
2831  }
2832 
2845  public ArrayList<BlackboardAttribute> getMatchingAttributes(String whereClause) throws TskCoreException {
2846  CaseDbConnection connection = connections.getConnection();
2848  Statement s = null;
2849  ResultSet rs = null;
2850  try {
2851  s = connection.createStatement();
2852  rs = connection.executeQuery(s, "SELECT blackboard_attributes.artifact_id AS artifact_id, "
2853  + "blackboard_attributes.source AS source, blackboard_attributes.context AS context, "
2854  + "blackboard_attributes.attribute_type_id AS attribute_type_id, "
2855  + "blackboard_attributes.value_type AS value_type, blackboard_attributes.value_byte AS value_byte, "
2856  + "blackboard_attributes.value_text AS value_text, blackboard_attributes.value_int32 AS value_int32, "
2857  + "blackboard_attributes.value_int64 AS value_int64, blackboard_attributes.value_double AS value_double "
2858  + "FROM blackboard_attributes " + whereClause); //NON-NLS
2859  ArrayList<BlackboardAttribute> matches = new ArrayList<BlackboardAttribute>();
2860  while (rs.next()) {
2862  // attribute type is cached, so this does not necessarily call to the db
2863  type = this.getAttributeType(rs.getInt("attribute_type_id"));
2865  rs.getLong("artifact_id"),
2866  type,
2867  rs.getString("source"),
2868  rs.getString("context"),
2869  rs.getInt("value_int32"),
2870  rs.getLong("value_int64"),
2871  rs.getDouble("value_double"),
2872  rs.getString("value_text"),
2873  rs.getBytes("value_byte"), this
2874  );
2875  matches.add(attr);
2876  }
2877  return matches;
2878  } catch (SQLException ex) {
2879  throw new TskCoreException("Error getting attributes using this where clause: " + whereClause, ex);
2880  } finally {
2881  closeResultSet(rs);
2882  closeStatement(s);
2883  connection.close();
2885  }
2886  }
2887 
2899  public ArrayList<BlackboardArtifact> getMatchingArtifacts(String whereClause) throws TskCoreException {
2900  CaseDbConnection connection = connections.getConnection();
2902  ResultSet rs = null;
2903  Statement s = null;
2904  try {
2905  s = connection.createStatement();
2906  rs = connection.executeQuery(s, "SELECT blackboard_artifacts.artifact_id AS artifact_id, "
2907  + "blackboard_artifacts.obj_id AS obj_id, blackboard_artifacts.artifact_type_id AS artifact_type_id, "
2908  + "blackboard_artifacts.review_status_id AS review_status_id "
2909  + "FROM blackboard_artifacts " + whereClause); //NON-NLS
2910  ArrayList<BlackboardArtifact> matches = new ArrayList<BlackboardArtifact>();
2911  while (rs.next()) {
2912  BlackboardArtifact.Type type;
2913  // artifact type is cached, so this does not necessarily call to the db
2914  type = this.getArtifactType(rs.getInt("artifact_type_id"));
2915  BlackboardArtifact artifact = new BlackboardArtifact(this, rs.getLong("artifact_id"),
2916  rs.getLong("obj_id"), type.getTypeID(), type.getTypeName(), type.getDisplayName(),
2917  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")));
2918  matches.add(artifact);
2919  }
2920  return matches;
2921  } catch (SQLException ex) {
2922  throw new TskCoreException("Error getting attributes using this where clause: " + whereClause, ex);
2923  } finally {
2924  closeResultSet(rs);
2925  closeStatement(s);
2926  connection.close();
2928  }
2929  }
2930 
2944  public BlackboardArtifact newBlackboardArtifact(int artifactTypeID, long obj_id) throws TskCoreException {
2945  BlackboardArtifact.Type type = getArtifactType(artifactTypeID);
2946  return newBlackboardArtifact(artifactTypeID, obj_id, type.getTypeName(), type.getDisplayName());
2947  }
2948 
2960  public BlackboardArtifact newBlackboardArtifact(ARTIFACT_TYPE artifactType, long obj_id) throws TskCoreException {
2961  return newBlackboardArtifact(artifactType.getTypeID(), obj_id, artifactType.getLabel(), artifactType.getDisplayName());
2962  }
2963 
2964  private BlackboardArtifact newBlackboardArtifact(int artifact_type_id, long obj_id, String artifactTypeName, String artifactDisplayName) throws TskCoreException {
2965  CaseDbConnection connection = connections.getConnection();
2967  ResultSet rs = null;
2968  try {
2969  PreparedStatement statement;
2970  if (dbType == DbType.POSTGRESQL) {
2971  statement = connection.getPreparedStatement(PREPARED_STATEMENT.POSTGRESQL_INSERT_ARTIFACT, Statement.RETURN_GENERATED_KEYS);
2972  statement.clearParameters();
2973  statement.setLong(1, obj_id);
2974  statement.setInt(2, artifact_type_id);
2975  } else {
2976  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_ARTIFACT, Statement.RETURN_GENERATED_KEYS);
2977  statement.clearParameters();
2978  this.nextArtifactId++;
2979  statement.setLong(1, this.nextArtifactId);
2980  statement.setLong(2, obj_id);
2981  statement.setInt(3, artifact_type_id);
2982  }
2983  connection.executeUpdate(statement);
2984  rs = statement.getGeneratedKeys();
2985  rs.next();
2986  return new BlackboardArtifact(this, rs.getLong(1), //last_insert_rowid()
2987  obj_id, artifact_type_id, artifactTypeName, artifactDisplayName, BlackboardArtifact.ReviewStatus.UNDECIDED, true);
2988  } catch (SQLException ex) {
2989  throw new TskCoreException("Error creating a blackboard artifact", ex);
2990  } finally {
2991  closeResultSet(rs);
2992  connection.close();
2994  }
2995  }
2996 
3009  boolean getContentHasChildren(Content content) throws TskCoreException {
3010  CaseDbConnection connection = connections.getConnection();
3012  ResultSet rs = null;
3013  try {
3014  // SELECT COUNT(obj_id) AS count FROM tsk_objects WHERE par_obj_id = ?
3015  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_CHILD_OBJECTS_BY_PARENT);
3016  statement.clearParameters();
3017  statement.setLong(1, content.getId());
3018  rs = connection.executeQuery(statement);
3019  boolean hasChildren = false;
3020  if (rs.next()) {
3021  hasChildren = rs.getInt("count") > 0;
3022  }
3023  return hasChildren;
3024  } catch (SQLException e) {
3025  throw new TskCoreException("Error checking for children of parent " + content, e);
3026  } finally {
3027  closeResultSet(rs);
3028  connection.close();
3030  }
3031  }
3032 
3045  int getContentChildrenCount(Content content) throws TskCoreException {
3046  CaseDbConnection connection = connections.getConnection();
3048  ResultSet rs = null;
3049  try {
3050  // SELECT COUNT(obj_id) AS count FROM tsk_objects WHERE par_obj_id = ?
3051  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_CHILD_OBJECTS_BY_PARENT);
3052  statement.clearParameters();
3053  statement.setLong(1, content.getId());
3054  rs = connection.executeQuery(statement);
3055  int countChildren = -1;
3056  if (rs.next()) {
3057  countChildren = rs.getInt("count");
3058  }
3059  return countChildren;
3060  } catch (SQLException e) {
3061  throw new TskCoreException("Error checking for children of parent " + content, e);
3062  } finally {
3063  closeResultSet(rs);
3064  connection.close();
3066  }
3067  }
3068 
3080  List<Content> getAbstractFileChildren(Content parent, TSK_DB_FILES_TYPE_ENUM type) throws TskCoreException {
3081  CaseDbConnection connection = connections.getConnection();
3083  ResultSet rs = null;
3084  try {
3085  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_PARENT_AND_TYPE);
3086  statement.clearParameters();
3087  long parentId = parent.getId();
3088  statement.setLong(1, parentId);
3089  statement.setShort(2, type.getFileType());
3090  rs = connection.executeQuery(statement);
3091  return fileChildren(rs, connection, parentId);
3092  } catch (SQLException ex) {
3093  throw new TskCoreException("Error getting AbstractFile children for Content", ex);
3094  } finally {
3095  closeResultSet(rs);
3096  connection.close();
3098  }
3099  }
3100 
3110  List<Content> getAbstractFileChildren(Content parent) throws TskCoreException {
3111  CaseDbConnection connection = connections.getConnection();
3113  ResultSet rs = null;
3114  try {
3115  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_PARENT);
3116  statement.clearParameters();
3117  long parentId = parent.getId();
3118  statement.setLong(1, parentId);
3119  rs = connection.executeQuery(statement);
3120  return fileChildren(rs, connection, parentId);
3121  } catch (SQLException ex) {
3122  throw new TskCoreException("Error getting AbstractFile children for Content", ex);
3123  } finally {
3124  closeResultSet(rs);
3125  connection.close();
3127  }
3128  }
3129 
3141  List<Long> getAbstractFileChildrenIds(Content parent, TSK_DB_FILES_TYPE_ENUM type) throws TskCoreException {
3142  CaseDbConnection connection = connections.getConnection();
3144  ResultSet rs = null;
3145  try {
3146  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_IDS_BY_PARENT_AND_TYPE);
3147  statement.clearParameters();
3148  statement.setLong(1, parent.getId());
3149  statement.setShort(2, type.getFileType());
3150  rs = connection.executeQuery(statement);
3151  List<Long> children = new ArrayList<Long>();
3152  while (rs.next()) {
3153  children.add(rs.getLong("obj_id"));
3154  }
3155  return children;
3156  } catch (SQLException ex) {
3157  throw new TskCoreException("Error getting AbstractFile children for Content", ex);
3158  } finally {
3159  closeResultSet(rs);
3160  connection.close();
3162  }
3163  }
3164 
3174  List<Long> getAbstractFileChildrenIds(Content parent) throws TskCoreException {
3175  CaseDbConnection connection = connections.getConnection();
3177  ResultSet rs = null;
3178  try {
3179  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_IDS_BY_PARENT);
3180  statement.clearParameters();
3181  statement.setLong(1, parent.getId());
3182  rs = connection.executeQuery(statement);
3183  List<Long> children = new ArrayList<Long>();
3184  while (rs.next()) {
3185  children.add(rs.getLong("obj_id"));
3186  }
3187  return children;
3188  } catch (SQLException ex) {
3189  throw new TskCoreException("Error getting AbstractFile children for Content", ex);
3190  } finally {
3191  closeResultSet(rs);
3192  connection.close();
3194  }
3195  }
3196 
3205  Collection<ObjectInfo> getChildrenInfo(Content c) throws TskCoreException {
3206  CaseDbConnection connection = connections.getConnection();
3208  Statement s = null;
3209  ResultSet rs = null;
3210  try {
3211  s = connection.createStatement();
3212  rs = connection.executeQuery(s, "SELECT tsk_objects.obj_id AS obj_id, tsk_objects.type AS type " //NON-NLS
3213  + "FROM tsk_objects LEFT JOIN tsk_files " //NON-NLS
3214  + "ON tsk_objects.obj_id = tsk_files.obj_id " //NON-NLS
3215  + "WHERE tsk_objects.par_obj_id = " + c.getId()
3216  + " ORDER BY tsk_objects.obj_id"); //NON-NLS
3217  Collection<ObjectInfo> infos = new ArrayList<ObjectInfo>();
3218  while (rs.next()) {
3219  infos.add(new ObjectInfo(rs.getLong("obj_id"), ObjectType.valueOf(rs.getShort("type")))); //NON-NLS
3220  }
3221  return infos;
3222  } catch (SQLException ex) {
3223  throw new TskCoreException("Error getting Children Info for Content", ex);
3224  } finally {
3225  closeResultSet(rs);
3226  closeStatement(s);
3227  connection.close();
3229  }
3230  }
3231 
3242  ObjectInfo getParentInfo(Content c) throws TskCoreException {
3243  // TODO: This should not throw an exception if Content has no parent,
3244  // return null instead.
3245  CaseDbConnection connection = connections.getConnection();
3247  Statement s = null;
3248  ResultSet rs = null;
3249  try {
3250  s = connection.createStatement();
3251  rs = connection.executeQuery(s, "SELECT parent.obj_id AS obj_id, parent.type AS type " //NON-NLS
3252  + "FROM tsk_objects AS parent INNER JOIN tsk_objects AS child " //NON-NLS
3253  + "ON child.par_obj_id = parent.obj_id " //NON-NLS
3254  + "WHERE child.obj_id = " + c.getId()); //NON-NLS
3255  if (rs.next()) {
3256  return new ObjectInfo(rs.getLong("obj_id"), ObjectType.valueOf(rs.getShort("type")));
3257  } else {
3258  throw new TskCoreException("Given content (id: " + c.getId() + ") has no parent");
3259  }
3260  } catch (SQLException ex) {
3261  throw new TskCoreException("Error getting Parent Info for Content", ex);
3262  } finally {
3263  closeResultSet(rs);
3264  closeStatement(s);
3265  connection.close();
3267  }
3268  }
3269 
3280  ObjectInfo getParentInfo(long contentId) throws TskCoreException {
3281  // TODO: This should not throw an exception if Content has no parent,
3282  // return null instead.
3283  CaseDbConnection connection = connections.getConnection();
3285  Statement s = null;
3286  ResultSet rs = null;
3287  try {
3288  s = connection.createStatement();
3289  rs = connection.executeQuery(s, "SELECT parent.obj_id AS obj_id, parent.type AS type " //NON-NLS
3290  + "FROM tsk_objects AS parent INNER JOIN tsk_objects AS child " //NON-NLS
3291  + "ON child.par_obj_id = parent.obj_id " //NON-NLS
3292  + "WHERE child.obj_id = " + contentId); //NON-NLS
3293  if (rs.next()) {
3294  return new ObjectInfo(rs.getLong("obj_id"), ObjectType.valueOf(rs.getShort("type")));
3295  } else {
3296  throw new TskCoreException("Given content (id: " + contentId + ") has no parent.");
3297  }
3298  } catch (SQLException ex) {
3299  throw new TskCoreException("Error getting Parent Info for Content: " + contentId, ex);
3300  } finally {
3301  closeResultSet(rs);
3302  closeStatement(s);
3303  connection.close();
3305  }
3306  }
3307 
3318  Directory getParentDirectory(FsContent fsc) throws TskCoreException {
3319  // TODO: This should not throw an exception if Content has no parent,
3320  // return null instead.
3321  if (fsc.isRoot()) {
3322  throw new TskCoreException("Given FsContent (id: " + fsc.getId() + ") is a root object (can't have parent directory).");
3323  } else {
3324  ObjectInfo parentInfo = getParentInfo(fsc);
3325  Directory parent = null;
3326  if (parentInfo.type == ObjectType.ABSTRACTFILE) {
3327  parent = getDirectoryById(parentInfo.id, fsc.getFileSystem());
3328  } else {
3329  throw new TskCoreException("Parent of FsContent (id: " + fsc.getId() + ") has wrong type to be directory: " + parentInfo.type);
3330  }
3331  return parent;
3332  }
3333  }
3334 
3346  public Content getContentById(long id) throws TskCoreException {
3347  CaseDbConnection connection = connections.getConnection();
3349  Statement s = null;
3350  ResultSet rs = null;
3351  try {
3352  s = connection.createStatement();
3353  rs = connection.executeQuery(s, "SELECT * FROM tsk_objects WHERE obj_id = " + id + " LIMIT 1"); //NON-NLS
3354  if (!rs.next()) {
3355  return null;
3356  }
3357 
3358  AbstractContent content = null;
3359  long parentId = rs.getLong("par_obj_id"); //NON-NLS
3360  final TskData.ObjectType type = TskData.ObjectType.valueOf(rs.getShort("type")); //NON-NLS
3361  switch (type) {
3362  case IMG:
3363  content = getImageById(id);
3364  break;
3365  case VS:
3366  content = getVolumeSystemById(id, parentId);
3367  break;
3368  case VOL:
3369  content = getVolumeById(id, parentId);
3370  break;
3371  case FS:
3372  content = getFileSystemById(id, parentId);
3373  break;
3374  case ABSTRACTFILE:
3375  content = getAbstractFileById(id);
3376  break;
3377  default:
3378  throw new TskCoreException("Could not obtain Content object with ID: " + id);
3379  }
3380  return content;
3381  } catch (SQLException ex) {
3382  throw new TskCoreException("Error getting Content by ID.", ex);
3383  } finally {
3384  closeResultSet(rs);
3385  closeStatement(s);
3386  connection.close();
3388  }
3389  }
3390 
3398  String getFilePath(long id) {
3399  CaseDbConnection connection;
3400  try {
3401  connection = connections.getConnection();
3402  } catch (TskCoreException ex) {
3403  logger.log(Level.SEVERE, "Error getting file path for file " + id, ex); //NON-NLS
3404  return null;
3405  }
3406  String filePath = null;
3408  ResultSet rs = null;
3409  try {
3410  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_LOCAL_PATH_FOR_FILE);
3411  statement.clearParameters();
3412  statement.setLong(1, id);
3413  rs = connection.executeQuery(statement);
3414  if (rs.next()) {
3415  filePath = rs.getString("path");
3416  }
3417  } catch (SQLException ex) {
3418  logger.log(Level.SEVERE, "Error getting file path for file " + id, ex); //NON-NLS
3419  } finally {
3420  closeResultSet(rs);
3421  connection.close();
3423  }
3424  return filePath;
3425  }
3426 
3434  TskData.EncodingType getEncodingType(long id) {
3435  CaseDbConnection connection;
3436  try {
3437  connection = connections.getConnection();
3438  } catch (TskCoreException ex) {
3439  logger.log(Level.SEVERE, "Error getting file path for file " + id, ex); //NON-NLS
3440  return null;
3441  }
3442  TskData.EncodingType type = TskData.EncodingType.NONE;
3444  ResultSet rs = null;
3445  try {
3446  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ENCODING_FOR_FILE);
3447  statement.clearParameters();
3448  statement.setLong(1, id);
3449  rs = connection.executeQuery(statement);
3450  if (rs.next()) {
3451  type = TskData.EncodingType.valueOf(rs.getInt(1));
3452  }
3453  } catch (SQLException ex) {
3454  logger.log(Level.SEVERE, "Error getting encoding type for file " + id, ex); //NON-NLS
3455  } finally {
3456  closeResultSet(rs);
3457  connection.close();
3459  }
3460  return type;
3461  }
3462 
3471  String getFileParentPath(long objectId, CaseDbConnection connection) {
3472  String parentPath = null;
3474  ResultSet rs = null;
3475  try {
3476  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_PATH_FOR_FILE);
3477  statement.clearParameters();
3478  statement.setLong(1, objectId);
3479  rs = connection.executeQuery(statement);
3480  if (rs.next()) {
3481  parentPath = rs.getString("parent_path");
3482  }
3483  } catch (SQLException ex) {
3484  logger.log(Level.SEVERE, "Error getting file parent_path for file " + objectId, ex); //NON-NLS
3485  } finally {
3486  closeResultSet(rs);
3488  }
3489  return parentPath;
3490  }
3491 
3500  String getFileName(long objectId, CaseDbConnection connection) {
3501  String fileName = null;
3503  ResultSet rs = null;
3504  try {
3505  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_NAME);
3506  statement.clearParameters();
3507  statement.setLong(1, objectId);
3508  rs = connection.executeQuery(statement);
3509  if (rs.next()) {
3510  fileName = rs.getString("name");
3511  }
3512  } catch (SQLException ex) {
3513  logger.log(Level.SEVERE, "Error getting file parent_path for file " + objectId, ex); //NON-NLS
3514  } finally {
3515  closeResultSet(rs);
3517  }
3518  return fileName;
3519  }
3520 
3531  DerivedFile.DerivedMethod getDerivedMethod(long id) throws TskCoreException {
3532  CaseDbConnection connection = connections.getConnection();
3533  DerivedFile.DerivedMethod method = null;
3535  ResultSet rs1 = null;
3536  ResultSet rs2 = null;
3537  try {
3538  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_DERIVED_FILE);
3539  statement.clearParameters();
3540  statement.setLong(1, id);
3541  rs1 = connection.executeQuery(statement);
3542  if (rs1.next()) {
3543  int method_id = rs1.getInt("derived_id");
3544  String rederive = rs1.getString("rederive");
3545  method = new DerivedFile.DerivedMethod(method_id, rederive);
3546  statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_DERIVATION_METHOD);
3547  statement.clearParameters();
3548  statement.setInt(1, method_id);
3549  rs2 = connection.executeQuery(statement);
3550  if (rs2.next()) {
3551  method.setToolName(rs2.getString("tool_name"));
3552  method.setToolVersion(rs2.getString("tool_version"));
3553  method.setOther(rs2.getString("other"));
3554  }
3555  }
3556  } catch (SQLException e) {
3557  logger.log(Level.SEVERE, "Error getting derived method for file: " + id, e); //NON-NLS
3558  } finally {
3559  closeResultSet(rs2);
3560  closeResultSet(rs1);
3561  connection.close();
3563  }
3564  return method;
3565  }
3566 
3578  CaseDbConnection connection = connections.getConnection();
3580  ResultSet rs = null;
3581  try {
3582  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_BY_ID);
3583  statement.clearParameters();
3584  statement.setLong(1, id);
3585  rs = connection.executeQuery(statement);
3586  List<AbstractFile> files = resultSetToAbstractFiles(rs, connection);
3587  if (files.size() > 0) {
3588  return files.get(0);
3589  } else {
3590  return null;
3591  }
3592  } catch (SQLException ex) {
3593  throw new TskCoreException("Error getting file by id, id = " + id, ex);
3594  } finally {
3595  closeResultSet(rs);
3596  connection.close();
3598  }
3599  }
3600 
3613  private long getFileSystemId(long fileId, CaseDbConnection connection) {
3615  ResultSet rs = null;
3616  long ret = -1;
3617  try {
3618  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_SYSTEM_BY_OBJECT);
3619  statement.clearParameters();
3620  statement.setLong(1, fileId);
3621  rs = connection.executeQuery(statement);
3622  if (rs.next()) {
3623  ret = rs.getLong("fs_obj_id");
3624  if (ret == 0) {
3625  ret = -1;
3626  }
3627  }
3628  } catch (SQLException e) {
3629  logger.log(Level.SEVERE, "Error checking file system id of a file, id = " + fileId, e); //NON-NLS
3630  } finally {
3631  closeResultSet(rs);
3633  }
3634  return ret;
3635  }
3636 
3648  public boolean isFileFromSource(Content dataSource, long fileId) throws TskCoreException {
3649  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
3650  CaseDbConnection connection = connections.getConnection();
3652  Statement statement = null;
3653  ResultSet resultSet = null;
3654  try {
3655  statement = connection.createStatement();
3656  resultSet = connection.executeQuery(statement, query);
3657  resultSet.next();
3658  return (resultSet.getLong("count") > 0L);
3659  } catch (SQLException ex) {
3660  throw new TskCoreException(String.format("Error executing query %s", query), ex);
3661  } finally {
3662  closeResultSet(resultSet);
3663  closeStatement(statement);
3664  connection.close();
3666  }
3667  }
3668 
3680  public List<AbstractFile> findFiles(Content dataSource, String fileName) throws TskCoreException {
3681  List<AbstractFile> files = new ArrayList<AbstractFile>();
3682  CaseDbConnection connection = connections.getConnection();
3684  ResultSet resultSet = null;
3685  try {
3686  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_DATA_SOURCE_AND_NAME);
3687  statement.clearParameters();
3688  statement.setString(1, fileName.toLowerCase());
3689  statement.setLong(2, dataSource.getId());
3690  resultSet = connection.executeQuery(statement);
3691  files.addAll(resultSetToAbstractFiles(resultSet, connection));
3692  } catch (SQLException e) {
3693  throw new TskCoreException(bundle.getString("SleuthkitCase.findFiles.exception.msg3.text"), e);
3694  } finally {
3695  closeResultSet(resultSet);
3696  connection.close();
3698  }
3699  return files;
3700  }
3701 
3715  public List<AbstractFile> findFiles(Content dataSource, String fileName, String dirName) throws TskCoreException {
3716  List<AbstractFile> files = new ArrayList<AbstractFile>();
3717  CaseDbConnection connection = connections.getConnection();
3719  ResultSet resultSet = null;
3720  try {
3721  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_DATA_SOURCE_AND_PARENT_PATH_AND_NAME);
3722  statement.clearParameters();
3723  statement.setString(1, fileName.toLowerCase());
3724  statement.setString(2, "%" + dirName.toLowerCase() + "%"); //NON-NLS
3725  statement.setLong(3, dataSource.getId());
3726  resultSet = connection.executeQuery(statement);
3727  files.addAll(resultSetToAbstractFiles(resultSet, connection));
3728  } catch (SQLException e) {
3729  throw new TskCoreException(bundle.getString("SleuthkitCase.findFiles3.exception.msg3.text"), e);
3730  } finally {
3731  closeResultSet(resultSet);
3732  connection.close();
3734  }
3735  return files;
3736  }
3737 
3749  public VirtualDirectory addVirtualDirectory(long parentId, String directoryName) throws TskCoreException {
3751  CaseDbTransaction localTrans = beginTransaction();
3752  try {
3753  VirtualDirectory newVD = addVirtualDirectory(parentId, directoryName, localTrans);
3754  localTrans.commit();
3755  return newVD;
3756  } catch (TskCoreException ex) {
3757  try {
3758  localTrans.rollback();
3759  } catch (TskCoreException ex2) {
3760  logger.log(Level.SEVERE, String.format("Failed to rollback transaction after exception: %s", ex.getMessage()), ex2);
3761  }
3762  throw ex;
3763  } finally {
3765  }
3766  }
3767 
3785  public VirtualDirectory addVirtualDirectory(long parentId, String directoryName, CaseDbTransaction transaction) throws TskCoreException {
3786  if (transaction == null) {
3787  throw new TskCoreException("Passed null CaseDbTransaction");
3788  }
3789 
3791  ResultSet resultSet = null;
3792  try {
3793  // Get the parent path.
3794  CaseDbConnection connection = transaction.getConnection();
3795  String parentPath = getFileParentPath(parentId, connection);
3796  if (parentPath == null) {
3797  parentPath = "/"; //NON-NLS
3798  }
3799  String parentName = getFileName(parentId, connection);
3800  if (parentName != null && !parentName.isEmpty()) {
3801  parentPath = parentPath + parentName + "/"; //NON-NLS
3802  }
3803 
3804  // Insert a row for the virtual directory into the tsk_objects table.
3805  // INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)
3806  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_OBJECT, Statement.RETURN_GENERATED_KEYS);
3807  statement.clearParameters();
3808  if (parentId != 0) {
3809  statement.setLong(1, parentId);
3810  } else {
3811  statement.setNull(1, java.sql.Types.BIGINT);
3812  }
3813  statement.setInt(2, TskData.ObjectType.ABSTRACTFILE.getObjectType());
3814  connection.executeUpdate(statement);
3815  resultSet = statement.getGeneratedKeys();
3816  resultSet.next();
3817  long newObjId = resultSet.getLong(1); //last_insert_rowid()
3818 
3819  // Insert a row for the virtual directory into the tsk_files table.
3820  // INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type,
3821  // dir_flags, meta_flags, size, ctime, crtime, atime, mtime, parent_path, data_source_obj_id)
3822  // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
3823  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
3824  statement.clearParameters();
3825  statement.setLong(1, newObjId);
3826 
3827  // If the parent is part of a file system, grab its file system ID
3828  if (0 != parentId) {
3829  long parentFs = this.getFileSystemId(parentId, connection);
3830  if (parentFs != -1) {
3831  statement.setLong(2, parentFs);
3832  } else {
3833  statement.setNull(2, java.sql.Types.BIGINT);
3834  }
3835  } else {
3836  statement.setNull(2, java.sql.Types.BIGINT);
3837  }
3838 
3839  // name
3840  statement.setString(3, directoryName);
3841 
3842  //type
3843  statement.setShort(4, TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType());
3844  statement.setShort(5, (short) 1);
3845 
3846  //flags
3848  statement.setShort(6, dirType.getValue());
3850  statement.setShort(7, metaType.getValue());
3851 
3852  //allocated
3854  statement.setShort(8, dirFlag.getValue());
3855  final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue()
3856  | TSK_FS_META_FLAG_ENUM.USED.getValue());
3857  statement.setShort(9, metaFlags);
3858 
3859  //size
3860  statement.setLong(10, 0);
3861 
3862  // nulls for params 11-14
3863  statement.setNull(11, java.sql.Types.BIGINT);
3864  statement.setNull(12, java.sql.Types.BIGINT);
3865  statement.setNull(13, java.sql.Types.BIGINT);
3866  statement.setNull(14, java.sql.Types.BIGINT);
3867 
3868  // parent path
3869  statement.setString(15, parentPath);
3870 
3871  // data source object id (same as object id if this is a data source)
3872  long dataSourceObjectId;
3873  if (0 == parentId) {
3874  dataSourceObjectId = newObjId;
3875  } else {
3876  dataSourceObjectId = getDataSourceObjectId(connection, parentId);
3877  }
3878  statement.setLong(16, dataSourceObjectId);
3879 
3880  connection.executeUpdate(statement);
3881 
3882  return new VirtualDirectory(this, newObjId, dataSourceObjectId, directoryName, dirType,
3883  metaType, dirFlag, metaFlags, null, FileKnown.UNKNOWN,
3884  parentPath);
3885  } catch (SQLException e) {
3886  throw new TskCoreException("Error creating virtual directory '" + directoryName + "'", e);
3887  } finally {
3888  closeResultSet(resultSet);
3890  }
3891  }
3892 
3912  public LocalFilesDataSource addLocalFilesDataSource(String deviceId, String rootDirectoryName, String timeZone, CaseDbTransaction transaction) throws TskCoreException {
3914  Statement statement = null;
3915  ResultSet resultSet = null;
3916  try {
3917  // Insert a row for the root virtual directory of the data source
3918  // into the tsk_objects table.
3919  // INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)
3920  CaseDbConnection connection = transaction.getConnection();
3921  PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_OBJECT, Statement.RETURN_GENERATED_KEYS);
3922  preparedStatement.clearParameters();
3923  preparedStatement.setNull(1, java.sql.Types.BIGINT);
3924  preparedStatement.setInt(2, TskData.ObjectType.ABSTRACTFILE.getObjectType());
3925  connection.executeUpdate(preparedStatement);
3926  resultSet = preparedStatement.getGeneratedKeys();
3927  resultSet.next();
3928  long newObjId = resultSet.getLong(1); //last_insert_rowid()
3929  resultSet.close();
3930  resultSet = null;
3931 
3932  // Insert a row for the virtual directory of the data source into
3933  // the data_source_info table.
3934  statement = connection.createStatement();
3935  statement.executeUpdate("INSERT INTO data_source_info (obj_id, device_id, time_zone) "
3936  + "VALUES(" + newObjId + ", '" + deviceId + "', '" + timeZone + "');");
3937 
3938  // Insert a row for the root virtual directory of the data source
3939  // into the tsk_files table. Note that its data source object id is
3940  // its own object id.
3941  // INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path,
3942  // dir_type, meta_type, dir_flags, meta_flags, size, ctime, crtime,
3943  // atime, mtime, parent_path, data_source_obj_id)
3944  // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
3945  preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
3946  preparedStatement.clearParameters();
3947  preparedStatement.setLong(1, newObjId);
3948  preparedStatement.setNull(2, java.sql.Types.BIGINT);
3949  preparedStatement.setString(3, rootDirectoryName);
3950  preparedStatement.setShort(4, TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType());
3951  preparedStatement.setShort(5, (short) 1);
3953  preparedStatement.setShort(6, TSK_FS_NAME_TYPE_ENUM.DIR.getValue());
3955  preparedStatement.setShort(7, metaType.getValue());
3957  preparedStatement.setShort(8, dirFlag.getValue());
3958  final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue()
3959  | TSK_FS_META_FLAG_ENUM.USED.getValue());
3960  preparedStatement.setShort(9, metaFlags);
3961  preparedStatement.setLong(10, 0);
3962  preparedStatement.setNull(11, java.sql.Types.BIGINT);
3963  preparedStatement.setNull(12, java.sql.Types.BIGINT);
3964  preparedStatement.setNull(13, java.sql.Types.BIGINT);
3965  preparedStatement.setNull(14, java.sql.Types.BIGINT);
3966  String parentPath = "/"; //NON-NLS
3967  preparedStatement.setString(15, parentPath);
3968  preparedStatement.setLong(16, newObjId);
3969  connection.executeUpdate(preparedStatement);
3970 
3971  VirtualDirectory rootDirectory = new VirtualDirectory(this,
3972  newObjId, newObjId, rootDirectoryName,
3973  dirType, metaType, dirFlag, metaFlags, null,
3974  FileKnown.UNKNOWN, parentPath);
3975  return new LocalFilesDataSource(deviceId, rootDirectory, timeZone);
3976 
3977  } catch (SQLException ex) {
3978  throw new TskCoreException(String.format("Error creating local files data source with device id %s and directory name %s", deviceId, rootDirectoryName), ex);
3979  } finally {
3980  closeResultSet(resultSet);
3981  closeStatement(statement);
3983  }
3984  }
3985 
3994  public List<VirtualDirectory> getVirtualDirectoryRoots() throws TskCoreException {
3995  CaseDbConnection connection = connections.getConnection();
3997  Statement s = null;
3998  ResultSet rs = null;
3999  try {
4000  s = connection.createStatement();
4001  rs = connection.executeQuery(s, "SELECT * FROM tsk_files WHERE" //NON-NLS
4002  + " type = " + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
4003  + " AND obj_id = data_source_obj_id"
4004  + " ORDER BY dir_type, LOWER(name)"); //NON-NLS
4005  List<VirtualDirectory> virtDirRootIds = new ArrayList<VirtualDirectory>();
4006  while (rs.next()) {
4007  virtDirRootIds.add(virtualDirectory(rs));
4008  }
4009  return virtDirRootIds;
4010  } catch (SQLException ex) {
4011  throw new TskCoreException("Error getting local files virtual folder id", ex);
4012  } finally {
4013  closeResultSet(rs);
4014  closeStatement(s);
4015  connection.close();
4017  }
4018  }
4019 
4032  public final List<LayoutFile> addLayoutFiles(Content parent, List<TskFileRange> fileRanges) throws TskCoreException {
4033  assert (null != fileRanges);
4034  if (null == fileRanges) {
4035  throw new TskCoreException("TskFileRange object is null");
4036  }
4037 
4038  assert (null != parent);
4039  if (null == parent) {
4040  throw new TskCoreException("Conent is null");
4041  }
4042 
4043  CaseDbTransaction transaction = null;
4044  Statement statement = null;
4045  ResultSet resultSet = null;
4047 
4048  try {
4049  transaction = beginTransaction();
4050  CaseDbConnection connection = transaction.getConnection();
4051 
4052  List<LayoutFile> fileRangeLayoutFiles = new ArrayList<LayoutFile>();
4053  for (TskFileRange fileRange : fileRanges) {
4054  /*
4055  * Insert a row for the Tsk file range into the tsk_objects
4056  * table: INSERT INTO tsk_objects (par_obj_id, type) VALUES (?,
4057  * ?)
4058  */
4059  PreparedStatement prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_OBJECT, Statement.RETURN_GENERATED_KEYS);
4060  prepStmt.clearParameters();
4061  prepStmt.setLong(1, parent.getId()); // par_obj_id
4062  prepStmt.setLong(2, TskData.ObjectType.ABSTRACTFILE.getObjectType()); // type
4063  connection.executeUpdate(prepStmt);
4064  resultSet = prepStmt.getGeneratedKeys();
4065  resultSet.next();
4066  long fileRangeId = resultSet.getLong(1); //last_insert_rowid()
4067  long end_byte_in_parent = fileRange.getByteStart() + fileRange.getByteLen() - 1;
4068  /*
4069  * Insert a row for the Tsk file range into the tsk_files table:
4070  * INSERT INTO tsk_files (obj_id, fs_obj_id, name, type,
4071  * has_path, dir_type, meta_type, dir_flags, meta_flags, size,
4072  * ctime, crtime, atime, mtime, parent_path, data_source_obj_id)
4073  * VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
4074  */
4075  prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
4076  prepStmt.clearParameters();
4077  prepStmt.setLong(1, fileRangeId); // obj_id from tsk_objects
4078  prepStmt.setNull(2, java.sql.Types.BIGINT); // fs_obj_id
4079  prepStmt.setString(3, "Unalloc_" + parent.getId() + "_" + fileRange.getByteStart() + "_" + end_byte_in_parent); // name of form Unalloc_[image obj_id]_[start byte in parent]_[end byte in parent]
4080  prepStmt.setShort(4, TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType()); // type
4081  prepStmt.setNull(5, java.sql.Types.BIGINT); // has_path
4082  prepStmt.setShort(6, TSK_FS_NAME_TYPE_ENUM.REG.getValue()); // dir_type
4083  prepStmt.setShort(7, TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue()); // meta_type
4084  prepStmt.setShort(8, TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue()); // dir_flags
4085  prepStmt.setShort(9, TSK_FS_META_FLAG_ENUM.UNALLOC.getValue()); // nmeta_flags
4086  prepStmt.setLong(10, fileRange.getByteLen()); // size
4087  prepStmt.setNull(11, java.sql.Types.BIGINT); // ctime
4088  prepStmt.setNull(12, java.sql.Types.BIGINT); // crtime
4089  prepStmt.setNull(13, java.sql.Types.BIGINT); // atime
4090  prepStmt.setNull(14, java.sql.Types.BIGINT); // mtime
4091  prepStmt.setNull(15, java.sql.Types.VARCHAR); // parent path
4092  prepStmt.setLong(16, parent.getId()); // data_source_obj_id
4093  connection.executeUpdate(prepStmt);
4094 
4095  /*
4096  * Insert a row in the tsk_layout_file table for each chunk of
4097  * the carved file. INSERT INTO tsk_file_layout (obj_id,
4098  * byte_start, byte_len, sequence) VALUES (?, ?, ?, ?)
4099  */
4100  prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LAYOUT_FILE);
4101  prepStmt.clearParameters();
4102  prepStmt.setLong(1, fileRangeId); // obj_id
4103  prepStmt.setLong(2, fileRange.getByteStart()); // byte_start
4104  prepStmt.setLong(3, fileRange.getByteLen()); // byte_len
4105  prepStmt.setLong(4, fileRange.getSequence()); // sequence
4106  connection.executeUpdate(prepStmt);
4107 
4108  /*
4109  * Create a layout file representation of the carved file.
4110  */
4111  fileRangeLayoutFiles.add(new LayoutFile(this,
4112  fileRangeId,
4113  parent.getId(),
4114  Long.toString(fileRange.getSequence()),
4119  TSK_FS_META_FLAG_ENUM.UNALLOC.getValue(),
4120  fileRange.getByteLen(),
4121  null,
4123  parent.getUniquePath(),
4124  null));
4125  }
4126 
4127  transaction.commit();
4128  return fileRangeLayoutFiles;
4129 
4130  } catch (SQLException ex) {
4131  if (null != transaction) {
4132  try {
4133  transaction.rollback();
4134  } catch (TskCoreException ex2) {
4135  logger.log(Level.SEVERE, String.format("Failed to rollback transaction after exception: %s", ex.getMessage()), ex2);
4136  }
4137  }
4138  throw new TskCoreException("Failed to add layout files to case database", ex);
4139 
4140  } catch (TskCoreException ex) {
4141  if (null != transaction) {
4142  try {
4143  transaction.rollback();
4144  } catch (TskCoreException ex2) {
4145  logger.log(Level.SEVERE, String.format("Failed to rollback transaction after exception: %s", ex.getMessage()), ex2);
4146  }
4147  }
4148  throw ex;
4149 
4150  } finally {
4151  closeResultSet(resultSet);
4152  closeStatement(statement);
4154  }
4155  }
4156 
4168  public final List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws TskCoreException {
4169  assert (null != carvingResult);
4170  if (null == carvingResult) {
4171  throw new TskCoreException("Carving is null");
4172  }
4173  assert (null != carvingResult.getParent());
4174  if (null == carvingResult.getParent()) {
4175  throw new TskCoreException("Carving result has null parent");
4176  }
4177  assert (null != carvingResult.getCarvedFiles());
4178  if (null == carvingResult.getCarvedFiles()) {
4179  throw new TskCoreException("Carving result has null carved files");
4180  }
4181  CaseDbTransaction transaction = null;
4182  Statement statement = null;
4183  ResultSet resultSet = null;
4185  long newCacheKey = 0; // Used to roll back cache if transaction is rolled back.
4186  try {
4187  transaction = beginTransaction();
4188  CaseDbConnection connection = transaction.getConnection();
4189 
4190  /*
4191  * Carved files are "re-parented" as children of the $CarvedFiles
4192  * virtual directory of the root file system, volume, or image
4193  * ancestor of the carved files parent, but if no such ancestor is
4194  * found, then the parent specified in the carving result is used.
4195  */
4196  Content root = carvingResult.getParent();
4197  while (null != root) {
4198  if (root instanceof FileSystem || root instanceof Volume || root instanceof Image) {
4199  break;
4200  }
4201  root = root.getParent();
4202  }
4203  if (null == root) {
4204  root = carvingResult.getParent();
4205  }
4206 
4207  /*
4208  * Get or create the $CarvedFiles virtual directory for the root
4209  * ancestor.
4210  */
4211  VirtualDirectory carvedFilesDir = rootIdsToCarvedFileDirs.get(root.getId());
4212  if (null == carvedFilesDir) {
4213  List<Content> rootChildren;
4214  if (root instanceof FileSystem) {
4215  rootChildren = ((FileSystem) root).getRootDirectory().getChildren();
4216  } else {
4217  rootChildren = root.getChildren();
4218  }
4219  for (Content child : rootChildren) {
4220  if (child instanceof VirtualDirectory && child.getName().equals(VirtualDirectory.NAME_CARVED)) {
4221  carvedFilesDir = (VirtualDirectory) child;
4222  break;
4223  }
4224  }
4225  if (null == carvedFilesDir) {
4226  long parId = root.getId();
4227  // $CarvedFiles should be a child of the root directory, not the file system
4228  if (root instanceof FileSystem) {
4229  Content rootDir = ((FileSystem) root).getRootDirectory();
4230  parId = rootDir.getId();
4231  }
4232  carvedFilesDir = addVirtualDirectory(parId, VirtualDirectory.NAME_CARVED, transaction);
4233  }
4234  newCacheKey = root.getId();
4235  rootIdsToCarvedFileDirs.put(newCacheKey, carvedFilesDir);
4236  }
4237 
4238  /*
4239  * Add the carved files to the database as children of the
4240  * $CarvedFile directory of the root ancestor.
4241  */
4242  String parentPath = getFileParentPath(carvedFilesDir.getId(), connection) + carvedFilesDir.getName() + "/";
4243  List<LayoutFile> carvedFiles = new ArrayList<LayoutFile>();
4244  for (CarvingResult.CarvedFile carvedFile : carvingResult.getCarvedFiles()) {
4245  /*
4246  * Insert a row for the carved file into the tsk_objects table:
4247  * INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)
4248  */
4249  PreparedStatement prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_OBJECT, Statement.RETURN_GENERATED_KEYS);
4250  prepStmt.clearParameters();
4251  prepStmt.setLong(1, carvedFilesDir.getId()); // par_obj_id
4252  prepStmt.setLong(2, TskData.ObjectType.ABSTRACTFILE.getObjectType()); // type
4253  connection.executeUpdate(prepStmt);
4254  resultSet = prepStmt.getGeneratedKeys();
4255  resultSet.next();
4256  long carvedFileId = resultSet.getLong(1); //last_insert_rowid()
4257 
4258  /*
4259  * Insert a row for the carved file into the tsk_files table:
4260  * INSERT INTO tsk_files (obj_id, fs_obj_id, name, type,
4261  * has_path, dir_type, meta_type, dir_flags, meta_flags, size,
4262  * ctime, crtime, atime, mtime, parent_path, data_source_obj_id)
4263  * VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
4264  */
4265  prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
4266  prepStmt.clearParameters();
4267  prepStmt.setLong(1, carvedFileId); // obj_id
4268  if (root instanceof FileSystem) {
4269  prepStmt.setLong(2, root.getId()); // fs_obj_id
4270  } else {
4271  prepStmt.setNull(2, java.sql.Types.BIGINT); // fs_obj_id
4272  }
4273  prepStmt.setString(3, carvedFile.getName()); // name
4274  prepStmt.setShort(4, TSK_DB_FILES_TYPE_ENUM.CARVED.getFileType()); // type
4275  prepStmt.setShort(5, (short) 1); // has_path
4276  prepStmt.setShort(6, TSK_FS_NAME_TYPE_ENUM.REG.getValue()); // dir_type
4277  prepStmt.setShort(7, TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue()); // meta_type
4278  prepStmt.setShort(8, TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue()); // dir_flags
4279  prepStmt.setShort(9, TSK_FS_META_FLAG_ENUM.UNALLOC.getValue()); // nmeta_flags
4280  prepStmt.setLong(10, carvedFile.getSizeInBytes()); // size
4281  prepStmt.setNull(11, java.sql.Types.BIGINT); // ctime
4282  prepStmt.setNull(12, java.sql.Types.BIGINT); // crtime
4283  prepStmt.setNull(13, java.sql.Types.BIGINT); // atime
4284  prepStmt.setNull(14, java.sql.Types.BIGINT); // mtime
4285  prepStmt.setString(15, parentPath); // parent path
4286  prepStmt.setLong(16, carvedFilesDir.getDataSourceObjectId()); // data_source_obj_id
4287  connection.executeUpdate(prepStmt);
4288 
4289  /*
4290  * Insert a row in the tsk_layout_file table for each chunk of
4291  * the carved file. INSERT INTO tsk_file_layout (obj_id,
4292  * byte_start, byte_len, sequence) VALUES (?, ?, ?, ?)
4293  */
4294  prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LAYOUT_FILE);
4295  for (TskFileRange tskFileRange : carvedFile.getLayoutInParent()) {
4296  prepStmt.clearParameters();
4297  prepStmt.setLong(1, carvedFileId); // obj_id
4298  prepStmt.setLong(2, tskFileRange.getByteStart()); // byte_start
4299  prepStmt.setLong(3, tskFileRange.getByteLen()); // byte_len
4300  prepStmt.setLong(4, tskFileRange.getSequence()); // sequence
4301  connection.executeUpdate(prepStmt);
4302  }
4303 
4304  /*
4305  * Create a layout file representation of the carved file.
4306  */
4307  carvedFiles.add(new LayoutFile(this,
4308  carvedFileId,
4309  carvedFilesDir.getDataSourceObjectId(),
4310  carvedFile.getName(),
4315  TSK_FS_META_FLAG_ENUM.UNALLOC.getValue(),
4316  carvedFile.getSizeInBytes(),
4317  null,
4319  parentPath,
4320  null));
4321  }
4322 
4323  transaction.commit();
4324  return carvedFiles;
4325 
4326  } catch (SQLException ex) {
4327  if (null != transaction) {
4328  try {
4329  transaction.rollback();
4330  } catch (TskCoreException ex2) {
4331  logger.log(Level.SEVERE, String.format("Failed to rollback transaction after exception: %s", ex.getMessage()), ex2);
4332  }
4333  if (0 != newCacheKey) {
4334  rootIdsToCarvedFileDirs.remove(newCacheKey);
4335  }
4336  }
4337  throw new TskCoreException("Failed to add carved files to case database", ex);
4338 
4339  } catch (TskCoreException ex) {
4340  if (null != transaction) {
4341  try {
4342  transaction.rollback();
4343  } catch (TskCoreException ex2) {
4344  logger.log(Level.SEVERE, String.format("Failed to rollback transaction after exception: %s", ex.getMessage()), ex2);
4345  }
4346  if (0 != newCacheKey) {
4347  rootIdsToCarvedFileDirs.remove(newCacheKey);
4348  }
4349  }
4350  throw ex;
4351 
4352  } finally {
4353  closeResultSet(resultSet);
4354  closeStatement(statement);
4356  }
4357  }
4358 
4389  public DerivedFile addDerivedFile(String fileName, String localPath,
4390  long size, long ctime, long crtime, long atime, long mtime,
4391  boolean isFile, AbstractFile parentFile,
4392  String rederiveDetails, String toolName, String toolVersion,
4393  String otherDetails, TskData.EncodingType encodingType) throws TskCoreException {
4394  CaseDbConnection connection = connections.getConnection();
4396  ResultSet rs = null;
4397  try {
4398  connection.beginTransaction();
4399 
4400  final long parentId = parentFile.getId();
4401  final String parentPath = parentFile.getParentPath() + parentFile.getName() + '/'; //NON-NLS
4402 
4403  // Insert a row for the derived file into the tsk_objects table.
4404  // INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)
4405  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_OBJECT, Statement.RETURN_GENERATED_KEYS);
4406  statement.clearParameters();
4407  statement.setLong(1, parentId);
4408  statement.setLong(2, TskData.ObjectType.ABSTRACTFILE.getObjectType());
4409  connection.executeUpdate(statement);
4410  rs = statement.getGeneratedKeys();
4411  rs.next();
4412  long newObjId = rs.getLong(1); //last_insert_rowid()
4413  rs.close();
4414  rs = null;
4415 
4416  // Insert a row for the virtual directory into the tsk_files table.
4417  // INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type,
4418  // dir_flags, meta_flags, size, ctime, crtime, atime, mtime, parent_path, data_source_obj_id)
4419  // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
4420  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
4421  statement.clearParameters();
4422  statement.setLong(1, newObjId);
4423 
4424  // If the parentFile is part of a file system, use its file system object ID.
4425  long fsObjId = this.getFileSystemId(parentId, connection);
4426  if (fsObjId != -1) {
4427  statement.setLong(2, fsObjId);
4428  } else {
4429  statement.setNull(2, java.sql.Types.BIGINT);
4430  }
4431  statement.setString(3, fileName);
4432 
4433  //type, has_path
4434  statement.setShort(4, TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.getFileType());
4435  statement.setShort(5, (short) 1);
4436 
4437  //flags
4439  statement.setShort(6, dirType.getValue());
4441  statement.setShort(7, metaType.getValue());
4442 
4443  //note: using alloc under assumption that derived files derive from alloc files
4445  statement.setShort(8, dirFlag.getValue());
4446  final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue()
4447  | TSK_FS_META_FLAG_ENUM.USED.getValue());
4448  statement.setShort(9, metaFlags);
4449 
4450  //size
4451  statement.setLong(10, size);
4452 
4453  //mactimes
4454  //long ctime, long crtime, long atime, long mtime,
4455  statement.setLong(11, ctime);
4456  statement.setLong(12, crtime);
4457  statement.setLong(13, atime);
4458  statement.setLong(14, mtime);
4459 
4460  //parent path
4461  statement.setString(15, parentPath);
4462 
4463  // root data source object id
4464  long dataSourceObjId = getDataSourceObjectId(connection, parentId);
4465  statement.setLong(16, dataSourceObjId);
4466 
4467  connection.executeUpdate(statement);
4468 
4469  //add localPath
4470  addFilePath(connection, newObjId, localPath, encodingType);
4471 
4472  connection.commitTransaction();
4473 
4474  //TODO add derived method to tsk_files_derived and tsk_files_derived_method
4475  return new DerivedFile(this, newObjId, dataSourceObjId, fileName, dirType, metaType, dirFlag, metaFlags,
4476  size, ctime, crtime, atime, mtime, null, null, parentPath, localPath, parentId, null, encodingType);
4477  } catch (SQLException ex) {
4478  connection.rollbackTransaction();
4479  throw new TskCoreException("Failed to add derived file to case database", ex);
4480  } finally {
4481  closeResultSet(rs);
4482  connection.close();
4484  }
4485  }
4486 
4506  public LocalFile addLocalFile(String fileName, String localPath,
4507  long size, long ctime, long crtime, long atime, long mtime,
4508  boolean isFile, TskData.EncodingType encodingType,
4509  AbstractFile parent) throws TskCoreException {
4511  CaseDbTransaction localTrans = beginTransaction();
4512  try {
4513  LocalFile created = addLocalFile(fileName, localPath, size, ctime, crtime, atime, mtime, isFile, encodingType, parent, localTrans);
4514  localTrans.commit();
4515  return created;
4516  } catch (TskCoreException ex) {
4517  try {
4518  localTrans.rollback();
4519  } catch (TskCoreException ex2) {
4520  logger.log(Level.SEVERE, String.format("Failed to rollback transaction after exception: %s", ex.getMessage()), ex2);
4521  }
4522  throw ex;
4523  } finally {
4525  }
4526  }
4527 
4552  public LocalFile addLocalFile(String fileName, String localPath,
4553  long size, long ctime, long crtime, long atime, long mtime,
4554  boolean isFile, TskData.EncodingType encodingType,
4555  AbstractFile parent, CaseDbTransaction transaction) throws TskCoreException {
4556 
4557  CaseDbConnection connection = transaction.getConnection();
4559  Statement queryStatement = null;
4560  ResultSet resultSet = null;
4561  try {
4562 
4563  // Insert a row for the local/logical file into the tsk_objects table.
4564  // INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)
4565  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_OBJECT, Statement.RETURN_GENERATED_KEYS);
4566  statement.clearParameters();
4567  statement.setLong(1, parent.getId());
4568  statement.setLong(2, TskData.ObjectType.ABSTRACTFILE.getObjectType());
4569  connection.executeUpdate(statement);
4570  resultSet = statement.getGeneratedKeys();
4571  if (!resultSet.next()) {
4572  throw new TskCoreException(String.format("Failed to INSERT local file %s (%s) with parent id %d in tsk_objects table", fileName, localPath, parent.getId()));
4573  }
4574  long objectId = resultSet.getLong(1); //last_insert_rowid()
4575  resultSet.close();
4576  resultSet = null;
4577 
4578  // Insert a row for the local/logical file into the tsk_files table.
4579  // INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type,
4580  // dir_flags, meta_flags, size, ctime, crtime, atime, mtime, parent_path, data_source_obj_id)
4581  // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
4582  statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE);
4583  statement.clearParameters();
4584  statement.setLong(1, objectId);
4585  statement.setNull(2, java.sql.Types.BIGINT); // Not part of a file system
4586  statement.setString(3, fileName);
4587  statement.setShort(4, TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.getFileType());
4588  statement.setShort(5, (short) 1);
4590  statement.setShort(6, dirType.getValue());
4592  statement.setShort(7, metaType.getValue());
4594  statement.setShort(8, dirFlag.getValue());
4595  short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue() | TSK_FS_META_FLAG_ENUM.USED.getValue());
4596  statement.setShort(9, metaFlags);
4597  statement.setLong(10, size);
4598  statement.setLong(11, ctime);
4599  statement.setLong(12, crtime);
4600  statement.setLong(13, atime);
4601  statement.setLong(14, mtime);
4602  String parentPath = parent.getParentPath() + parent.getName() + "/"; //NON-NLS
4603  statement.setString(15, parentPath);
4604  long dataSourceObjId = getDataSourceObjectId(connection, parent.getId()); // RJCTODO: Let this be passed in or make a story
4605  statement.setLong(16, dataSourceObjId);
4606  connection.executeUpdate(statement);
4607  addFilePath(connection, objectId, localPath, encodingType);
4608  return new LocalFile(this,
4609  objectId,
4610  fileName,
4612  dirType,
4613  metaType,
4614  dirFlag,
4615  metaFlags,
4616  size,
4617  ctime, crtime, atime, mtime,
4618  null, null, null,
4619  parent.getId(), parentPath,
4620  dataSourceObjId,
4621  localPath,
4622  encodingType);
4623 
4624  } catch (SQLException ex) {
4625  throw new TskCoreException(String.format("Failed to INSERT local file %s (%s) with parent id %d in tsk_files table", fileName, localPath, parent.getId()), ex);
4626  } finally {
4627  closeResultSet(resultSet);
4628  closeStatement(queryStatement);
4630  }
4631  }
4632 
4645  private long getDataSourceObjectId(CaseDbConnection connection, long objectId) throws TskCoreException {
4647  Statement statement = null;
4648  ResultSet resultSet = null;
4649  try {
4650  statement = connection.createStatement();
4651  long dataSourceObjId;
4652  long ancestorId = objectId;
4653  do {
4654  dataSourceObjId = ancestorId;
4655  String query = String.format("SELECT par_obj_id FROM tsk_objects WHERE obj_id = %s;", ancestorId);
4656  resultSet = statement.executeQuery(query);
4657  if (resultSet.next()) {
4658  ancestorId = resultSet.getLong("par_obj_id");
4659  } else {
4660  throw new TskCoreException(String.format("tsk_objects table is corrupt, SQL query returned no result: %s", query));
4661  }
4662  resultSet.close();
4663  resultSet = null;
4664  } while (0 != ancestorId); // Not NULL
4665  return dataSourceObjId;
4666  } catch (SQLException ex) {
4667  throw new TskCoreException(String.format("Error finding root data source for object (obj_id = %d)", objectId), ex);
4668  } finally {
4669  closeResultSet(resultSet);
4670  closeStatement(statement);
4672  }
4673  }
4674 
4685  private void addFilePath(CaseDbConnection connection, long objId, String path, TskData.EncodingType type) throws SQLException {
4686  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LOCAL_PATH);
4687  statement.clearParameters();
4688  statement.setLong(1, objId);
4689  statement.setString(2, path);
4690  statement.setInt(3, type.getType());
4691  connection.executeUpdate(statement);
4692  }
4693 
4709  public List<AbstractFile> findFiles(Content dataSource, String fileName, AbstractFile parentFile) throws TskCoreException {
4710  return findFiles(dataSource, fileName, parentFile.getName());
4711  }
4712 
4724  public long countFilesWhere(String sqlWhereClause) throws TskCoreException {
4725  CaseDbConnection connection = connections.getConnection();
4727  Statement s = null;
4728  ResultSet rs = null;
4729  try {
4730  s = connection.createStatement();
4731  rs = connection.executeQuery(s, "SELECT COUNT(*) AS count FROM tsk_files WHERE " + sqlWhereClause); //NON-NLS
4732  rs.next();
4733  return rs.getLong("count");
4734  } catch (SQLException e) {
4735  throw new TskCoreException("SQLException thrown when calling 'SleuthkitCase.countFilesWhere().", e);
4736  } finally {
4737  closeResultSet(rs);
4738  closeStatement(s);
4739  connection.close();
4741  }
4742  }
4743 
4761  public List<AbstractFile> findAllFilesWhere(String sqlWhereClause) throws TskCoreException {
4762  CaseDbConnection connection = connections.getConnection();
4764  Statement s = null;
4765  ResultSet rs = null;
4766  try {
4767  s = connection.createStatement();
4768  rs = connection.executeQuery(s, "SELECT * FROM tsk_files WHERE " + sqlWhereClause); //NON-NLS
4769  return resultSetToAbstractFiles(rs, connection);
4770  } catch (SQLException e) {
4771  throw new TskCoreException("SQLException thrown when calling 'SleuthkitCase.findAllFilesWhere(): " + sqlWhereClause, e);
4772  } finally {
4773  closeResultSet(rs);
4774  closeStatement(s);
4775  connection.close();
4777  }
4778  }
4779 
4792  public List<Long> findAllFileIdsWhere(String sqlWhereClause) throws TskCoreException {
4793  CaseDbConnection connection = connections.getConnection();
4795  Statement s = null;
4796  ResultSet rs = null;
4797  try {
4798  s = connection.createStatement();
4799  rs = connection.executeQuery(s, "SELECT obj_id FROM tsk_files WHERE " + sqlWhereClause); //NON-NLS
4800  List<Long> ret = new ArrayList<Long>();
4801  while (rs.next()) {
4802  ret.add(rs.getLong("obj_id"));
4803  }
4804  return ret;
4805  } catch (SQLException e) {
4806  throw new TskCoreException("SQLException thrown when calling 'SleuthkitCase.findAllFileIdsWhere(): " + sqlWhereClause, e);
4807  } finally {
4808  closeResultSet(rs);
4809  closeStatement(s);
4810  connection.close();
4812  }
4813  }
4814 
4826  public List<AbstractFile> openFiles(Content dataSource, String filePath) throws TskCoreException {
4827 
4828  // get the non-unique path (strip of image and volume path segments, if
4829  // the exist.
4830  String path = AbstractFile.createNonUniquePath(filePath).toLowerCase();
4831 
4832  // split the file name from the parent path
4833  int lastSlash = path.lastIndexOf('/'); //NON-NLS
4834 
4835  // if the last slash is at the end, strip it off
4836  if (lastSlash == path.length()) {
4837  path = path.substring(0, lastSlash - 1);
4838  lastSlash = path.lastIndexOf('/'); //NON-NLS
4839  }
4840 
4841  String parentPath = path.substring(0, lastSlash);
4842  String fileName = path.substring(lastSlash);
4843 
4844  return findFiles(dataSource, fileName, parentPath);
4845  }
4846 
4857  public List<TskFileRange> getFileRanges(long id) throws TskCoreException {
4858  CaseDbConnection connection = connections.getConnection();
4860  Statement s = null;
4861  ResultSet rs = null;
4862  try {
4863  s = connection.createStatement();
4864  rs = connection.executeQuery(s, "SELECT * FROM tsk_file_layout WHERE obj_id = " + id + " ORDER BY sequence");
4865  List<TskFileRange> ranges = new ArrayList<TskFileRange>();
4866  while (rs.next()) {
4867  TskFileRange range = new TskFileRange(rs.getLong("byte_start"), //NON-NLS
4868  rs.getLong("byte_len"), rs.getLong("sequence")); //NON-NLS
4869  ranges.add(range);
4870  }
4871  return ranges;
4872  } catch (SQLException ex) {
4873  throw new TskCoreException("Error getting TskFileLayoutRanges by id, id = " + id, ex);
4874  } finally {
4875  closeResultSet(rs);
4876  closeStatement(s);
4877  connection.close();
4879  }
4880  }
4881 
4892  public Image getImageById(long id) throws TskCoreException {
4893  CaseDbConnection connection = connections.getConnection();
4895  Statement s1 = null;
4896  ResultSet rs1 = null;
4897  Statement s2 = null;
4898  ResultSet rs2 = null;
4899  try {
4900  s1 = connection.createStatement();
4901  rs1 = connection.executeQuery(s1, "SELECT * FROM tsk_image_info WHERE obj_id = " + id); //NON-NLS
4902  if (rs1.next()) {
4903  s2 = connection.createStatement();
4904  rs2 = connection.executeQuery(s2, "SELECT * FROM tsk_image_names WHERE obj_id = " + rs1.getLong("obj_id")); //NON-NLS
4905  List<String> imagePaths = new ArrayList<String>();
4906  while (rs2.next()) {
4907  imagePaths.add(rs2.getString("name"));
4908  }
4909  long obj_id = rs1.getLong("obj_id"); //NON-NLS
4910  long type = rs1.getLong("type"); //NON-NLS
4911  long ssize = rs1.getLong("ssize"); //NON-NLS
4912  String tzone = rs1.getString("tzone"); //NON-NLS
4913  String md5 = "";
4914  if (getSchemaVersion() > 2) {
4915  md5 = rs1.getString("md5"); //NON-NLS
4916  }
4917  String name = rs1.getString("display_name");
4918  if (name == null) {
4919  if (imagePaths.size() > 0) {
4920  String path = imagePaths.get(0);
4921  name = (new java.io.File(path)).getName();
4922  } else {
4923  name = "";
4924  }
4925  }
4926  return new Image(this, obj_id, type, ssize, name,
4927  imagePaths.toArray(new String[imagePaths.size()]), tzone, md5);
4928  } else {
4929  throw new TskCoreException("No image found for id: " + id);
4930  }
4931  } catch (SQLException ex) {
4932  throw new TskCoreException("Error getting Image by id, id = " + id, ex);
4933  } finally {
4934  closeResultSet(rs2);
4935  closeStatement(s2);
4936  closeResultSet(rs1);
4937  closeStatement(s1);
4938  connection.close();
4940  }
4941  }
4942 
4954  VolumeSystem getVolumeSystemById(long id, Image parent) throws TskCoreException {
4955  CaseDbConnection connection = connections.getConnection();
4957  Statement s = null;
4958  ResultSet rs = null;
4959  try {
4960  s = connection.createStatement();
4961  rs = connection.executeQuery(s, "SELECT * FROM tsk_vs_info " //NON-NLS
4962  + "where obj_id = " + id); //NON-NLS
4963  if (rs.next()) {
4964  long type = rs.getLong("vs_type"); //NON-NLS
4965  long imgOffset = rs.getLong("img_offset"); //NON-NLS
4966  long blockSize = rs.getLong("block_size"); //NON-NLS
4967  VolumeSystem vs = new VolumeSystem(this, id, "", type, imgOffset, blockSize);
4968  vs.setParent(parent);
4969  return vs;
4970  } else {
4971  throw new TskCoreException("No volume system found for id:" + id);
4972  }
4973  } catch (SQLException ex) {
4974  throw new TskCoreException("Error getting Volume System by ID.", ex);
4975  } finally {
4976  closeResultSet(rs);
4977  closeStatement(s);
4978  connection.close();
4980  }
4981  }
4982 
4991  VolumeSystem getVolumeSystemById(long id, long parentId) throws TskCoreException {
4992  VolumeSystem vs = getVolumeSystemById(id, null);
4993  vs.setParentId(parentId);
4994  return vs;
4995  }
4996 
5008  FileSystem getFileSystemById(long id, Image parent) throws TskCoreException {
5009  return getFileSystemByIdHelper(id, parent);
5010  }
5011 
5020  FileSystem getFileSystemById(long id, long parentId) throws TskCoreException {
5021  Volume vol = null;
5022  FileSystem fs = getFileSystemById(id, vol);
5023  fs.setParentId(parentId);
5024  return fs;
5025  }
5026 
5038  FileSystem getFileSystemById(long id, Volume parent) throws TskCoreException {
5039  return getFileSystemByIdHelper(id, parent);
5040  }
5041 
5054  // see if we already have it
5055  // @@@ NOTE: this is currently kind of bad in that we are ignoring the parent value,
5056  // but it should be the same...
5057  synchronized (fileSystemIdMap) {
5058  if (fileSystemIdMap.containsKey(id)) {
5059  return fileSystemIdMap.get(id);
5060  }
5061  }
5062  CaseDbConnection connection = connections.getConnection();
5064  Statement s = null;
5065  ResultSet rs = null;
5066  try {
5067  s = connection.createStatement();
5068  rs = connection.executeQuery(s, "SELECT * FROM tsk_fs_info " //NON-NLS
5069  + "where obj_id = " + id); //NON-NLS
5070  if (rs.next()) {
5071  TskData.TSK_FS_TYPE_ENUM fsType = TskData.TSK_FS_TYPE_ENUM.valueOf(rs.getInt("fs_type")); //NON-NLS
5072  FileSystem fs = new FileSystem(this, rs.getLong("obj_id"), "", rs.getLong("img_offset"), //NON-NLS
5073  fsType, rs.getLong("block_size"), rs.getLong("block_count"), //NON-NLS
5074  rs.getLong("root_inum"), rs.getLong("first_inum"), rs.getLong("last_inum")); //NON-NLS
5075  fs.setParent(parent);
5076  // save it for the next call
5077  synchronized (fileSystemIdMap) {
5078  fileSystemIdMap.put(id, fs);
5079  }
5080  return fs;
5081  } else {
5082  throw new TskCoreException("No file system found for id:" + id);
5083  }
5084  } catch (SQLException ex) {
5085  throw new TskCoreException("Error getting File System by ID", ex);
5086  } finally {
5087  closeResultSet(rs);
5088  closeStatement(s);
5089  connection.close();
5091  }
5092  }
5093 
5105  Volume getVolumeById(long id, VolumeSystem parent) throws TskCoreException {
5106  CaseDbConnection connection = connections.getConnection();
5108  Statement s = null;
5109  ResultSet rs = null;
5110  try {
5111  s = connection.createStatement();
5112  rs = connection.executeQuery(s, "SELECT * FROM tsk_vs_parts " //NON-NLS
5113  + "where obj_id = " + id); //NON-NLS
5114  if (rs.next()) {
5122  String description;
5123  try {
5124  description = rs.getString("desc");
5125  } catch (Exception ex) {
5126  description = rs.getString("descr");
5127  }
5128  Volume vol = new Volume(this, rs.getLong("obj_id"), rs.getLong("addr"), //NON-NLS
5129  rs.getLong("start"), rs.getLong("length"), rs.getLong("flags"), //NON-NLS
5130  description);
5131  vol.setParent(parent);
5132  return vol;
5133  } else {
5134  throw new TskCoreException("No volume found for id:" + id);
5135  }
5136  } catch (SQLException ex) {
5137  throw new TskCoreException("Error getting Volume by ID", ex);
5138  } finally {
5139  closeResultSet(rs);
5140  closeStatement(s);
5141  connection.close();
5143  }
5144  }
5145 
5154  Volume getVolumeById(long id, long parentId) throws TskCoreException {
5155  Volume vol = getVolumeById(id, null);
5156  vol.setParentId(parentId);
5157  return vol;
5158  }
5159 
5171  Directory getDirectoryById(long id, FileSystem parentFs) throws TskCoreException {
5172  CaseDbConnection connection = connections.getConnection();
5174  Statement s = null;
5175  ResultSet rs = null;
5176  try {
5177  s = connection.createStatement();
5178  rs = connection.executeQuery(s, "SELECT * FROM tsk_files " //NON-NLS
5179  + "WHERE obj_id = " + id);
5180  Directory temp = null; //NON-NLS
5181  if (rs.next()) {
5182  final short type = rs.getShort("type"); //NON-NLS
5183  if (type == TSK_DB_FILES_TYPE_ENUM.FS.getFileType()) {
5184  if (rs.getShort("meta_type") == TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()) { //NON-NLS
5185  temp = directory(rs, parentFs);
5186  }
5187  } else if (type == TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()) {
5188  throw new TskCoreException("Expecting an FS-type directory, got virtual, id: " + id);
5189  }
5190  } else {
5191  throw new TskCoreException("No Directory found for id:" + id);
5192  }
5193  return temp;
5194  } catch (SQLException ex) {
5195  throw new TskCoreException("Error getting Directory by ID", ex);
5196  } finally {
5197  closeResultSet(rs);
5198  closeStatement(s);
5199  connection.close();
5201  }
5202  }
5203 
5211  public Collection<FileSystem> getFileSystems(Image image) {
5212  List<FileSystem> fileSystems = new ArrayList<FileSystem>();
5213  CaseDbConnection connection;
5214  try {
5215  connection = connections.getConnection();
5216  } catch (TskCoreException ex) {
5217  logger.log(Level.SEVERE, "Error getting file systems for image " + image.getId(), ex); //NON-NLS
5218  return fileSystems;
5219  }
5221  Statement s = null;
5222  ResultSet rs = null;
5223  try {
5224  s = connection.createStatement();
5225 
5226  // Get all the file systems.
5227  List<FileSystem> allFileSystems = new ArrayList<FileSystem>();
5228  try {
5229  rs = connection.executeQuery(s, "SELECT * FROM tsk_fs_info"); //NON-NLS
5230  while (rs.next()) {
5231  TskData.TSK_FS_TYPE_ENUM fsType = TskData.TSK_FS_TYPE_ENUM.valueOf(rs.getInt("fs_type")); //NON-NLS
5232  FileSystem fs = new FileSystem(this, rs.getLong("obj_id"), "", rs.getLong("img_offset"), //NON-NLS
5233  fsType, rs.getLong("block_size"), rs.getLong("block_count"), //NON-NLS
5234  rs.getLong("root_inum"), rs.getLong("first_inum"), rs.getLong("last_inum")); //NON-NLS
5235  fs.setParent(null);
5236  allFileSystems.add(fs);
5237  }
5238  } catch (SQLException ex) {
5239  logger.log(Level.SEVERE, "There was a problem while trying to obtain all file systems", ex); //NON-NLS
5240  } finally {
5241  closeResultSet(rs);
5242  rs = null;
5243  }
5244 
5245  // For each file system, find the image to which it belongs by iteratively
5246  // climbing the tsk_ojbects hierarchy only taking those file systems
5247  // that belong to this image.
5248  for (FileSystem fs : allFileSystems) {
5249  Long imageID = null;
5250  Long currentObjID = fs.getId();
5251  while (imageID == null) {
5252  try {
5253  rs = connection.executeQuery(s, "SELECT * FROM tsk_objects WHERE tsk_objects.obj_id = " + currentObjID); //NON-NLS
5254  rs.next();
5255  currentObjID = rs.getLong("par_obj_id"); //NON-NLS
5256  if (rs.getInt("type") == TskData.ObjectType.IMG.getObjectType()) { //NON-NLS
5257  imageID = rs.getLong("obj_id"); //NON-NLS
5258  }
5259  } catch (SQLException ex) {
5260  logger.log(Level.SEVERE, "There was a problem while trying to obtain this image's file systems", ex); //NON-NLS
5261  } finally {
5262  closeResultSet(rs);
5263  rs = null;
5264  }
5265  }
5266 
5267  // see if imageID is this image'statement ID
5268  if (imageID == image.getId()) {
5269  fileSystems.add(fs);
5270  }
5271  }
5272  } catch (SQLException ex) {
5273  logger.log(Level.SEVERE, "Error getting case database connection", ex); //NON-NLS
5274  } finally {
5275  closeResultSet(rs);
5276  closeStatement(s);
5277  connection.close();
5279  }
5280  return fileSystems;
5281  }
5282 
5293  List<Content> getImageChildren(Image img) throws TskCoreException {
5294  Collection<ObjectInfo> childInfos = getChildrenInfo(img);
5295  List<Content> children = new ArrayList<Content>();
5296  for (ObjectInfo info : childInfos) {
5297  if (info.type == ObjectType.VS) {
5298  children.add(getVolumeSystemById(info.id, img));
5299  } else if (info.type == ObjectType.FS) {
5300  children.add(getFileSystemById(info.id, img));
5301  } else if (info.type == ObjectType.ABSTRACTFILE) {
5302  AbstractFile f = getAbstractFileById(info.id);
5303  if (f != null) {
5304  children.add(f);
5305  }
5306  } else {
5307  throw new TskCoreException("Image has child of invalid type: " + info.type);
5308  }
5309  }
5310  return children;
5311  }
5312 
5323  List<Long> getImageChildrenIds(Image img) throws TskCoreException {
5324  Collection<ObjectInfo> childInfos = getChildrenInfo(img);
5325  List<Long> children = new ArrayList<Long>();
5326  for (ObjectInfo info : childInfos) {
5327  if (info.type == ObjectType.VS
5328  || info.type == ObjectType.FS
5329  || info.type == ObjectType.ABSTRACTFILE) {
5330  children.add(info.id);
5331  } else {
5332  throw new TskCoreException("Image has child of invalid type: " + info.type);
5333  }
5334  }
5335  return children;
5336  }
5337 
5348  List<Content> getVolumeSystemChildren(VolumeSystem vs) throws TskCoreException {
5349  Collection<ObjectInfo> childInfos = getChildrenInfo(vs);
5350  List<Content> children = new ArrayList<Content>();
5351  for (ObjectInfo info : childInfos) {
5352  if (null != info.type) {
5353  switch (info.type) {
5354  case VOL:
5355  children.add(getVolumeById(info.id, vs));
5356  break;
5357  case ABSTRACTFILE:
5358  AbstractFile f = getAbstractFileById(info.id);
5359  if (f != null) {
5360  children.add(f);
5361  }
5362  break;
5363  default:
5364  throw new TskCoreException("VolumeSystem has child of invalid type: " + info.type);
5365  }
5366  }
5367  }
5368  return children;
5369  }
5370 
5381  List<Long> getVolumeSystemChildrenIds(VolumeSystem vs) throws TskCoreException {
5382  Collection<ObjectInfo> childInfos = getChildrenInfo(vs);
5383  List<Long> children = new ArrayList<Long>();
5384  for (ObjectInfo info : childInfos) {
5385  if (info.type == ObjectType.VOL || info.type == ObjectType.ABSTRACTFILE) {
5386  children.add(info.id);
5387  } else {
5388  throw new TskCoreException("VolumeSystem has child of invalid type: " + info.type);
5389  }
5390  }
5391  return children;
5392  }
5393 
5404  List<Content> getVolumeChildren(Volume vol) throws TskCoreException {
5405  Collection<ObjectInfo> childInfos = getChildrenInfo(vol);
5406  List<Content> children = new ArrayList<Content>();
5407  for (ObjectInfo info : childInfos) {
5408  if (null != info.type) {
5409  switch (info.type) {
5410  case FS:
5411  children.add(getFileSystemById(info.id, vol));
5412  break;
5413  case ABSTRACTFILE:
5414  AbstractFile f = getAbstractFileById(info.id);
5415  if (f != null) {
5416  children.add(f);
5417  }
5418  break;
5419  default:
5420  throw new TskCoreException("Volume has child of invalid type: " + info.type);
5421  }
5422  }
5423  }
5424  return children;
5425  }
5426 
5437  List<Long> getVolumeChildrenIds(Volume vol) throws TskCoreException {
5438  final Collection<ObjectInfo> childInfos = getChildrenInfo(vol);
5439  final List<Long> children = new ArrayList<Long>();
5440  for (ObjectInfo info : childInfos) {
5441  if (info.type == ObjectType.FS || info.type == ObjectType.ABSTRACTFILE) {
5442  children.add(info.id);
5443  } else {
5444  throw new TskCoreException("Volume has child of invalid type: " + info.type);
5445  }
5446  }
5447  return children;
5448  }
5449 
5463  public Image addImageInfo(long deviceObjId, List<String> imageFilePaths, String timeZone) throws TskCoreException {
5464  long imageId = this.caseHandle.addImageInfo(deviceObjId, imageFilePaths, timeZone);
5465  return getImageById(imageId);
5466  }
5467 
5477  public Map<Long, List<String>> getImagePaths() throws TskCoreException {
5478  CaseDbConnection connection = connections.getConnection();
5480  Statement s1 = null;
5481  Statement s2 = null;
5482  ResultSet rs1 = null;
5483  ResultSet rs2 = null;
5484  try {
5485  s1 = connection.createStatement();
5486  rs1 = connection.executeQuery(s1, "SELECT obj_id FROM tsk_image_info"); //NON-NLS
5487  s2 = connection.createStatement();
5488  Map<Long, List<String>> imgPaths = new LinkedHashMap<Long, List<String>>();
5489  while (rs1.next()) {
5490  long obj_id = rs1.getLong("obj_id"); //NON-NLS
5491  rs2 = connection.executeQuery(s2, "SELECT * FROM tsk_image_names WHERE obj_id = " + obj_id); //NON-NLS
5492  List<String> paths = new ArrayList<String>();
5493  while (rs2.next()) {
5494  paths.add(rs2.getString("name"));
5495  }
5496  rs2.close();
5497  rs2 = null;
5498  imgPaths.put(obj_id, paths);
5499  }
5500  return imgPaths;
5501  } catch (SQLException ex) {
5502  throw new TskCoreException("Error getting image paths.", ex);
5503  } finally {
5504  closeResultSet(rs2);
5505  closeStatement(s2);
5506  closeResultSet(rs1);
5507  closeStatement(s1);
5508  connection.close();
5510  }
5511  }
5512 
5519  public List<Image> getImages() throws TskCoreException {
5520  CaseDbConnection connection = connections.getConnection();
5522  Statement s = null;
5523  ResultSet rs = null;
5524  try {
5525  s = connection.createStatement();
5526  rs = connection.executeQuery(s, "SELECT obj_id FROM tsk_image_info"); //NON-NLS
5527  Collection<Long> imageIDs = new ArrayList<Long>();
5528  while (rs.next()) {
5529  imageIDs.add(rs.getLong("obj_id")); //NON-NLS
5530  }
5531  List<Image> images = new ArrayList<Image>();
5532  for (long id : imageIDs) {
5533  images.add(getImageById(id));
5534  }
5535  return images;
5536  } catch (SQLException ex) {
5537  throw new TskCoreException("Error retrieving images.", ex);
5538  } finally {
5539  closeResultSet(rs);
5540  closeStatement(s);
5541  connection.close();
5543  }
5544  }
5545 
5556  public void setImagePaths(long obj_id, List<String> paths) throws TskCoreException {
5557  CaseDbConnection connection = connections.getConnection();
5559  Statement statement = null;
5560  try {
5561  connection.beginTransaction();
5562  statement = connection.createStatement();
5563  connection.executeUpdate(statement, "DELETE FROM tsk_image_names WHERE obj_id = " + obj_id); //NON-NLS
5564  for (int i = 0; i < paths.size(); i++) {
5565  connection.executeUpdate(statement, "INSERT INTO tsk_image_names VALUES (" + obj_id + ", '" + paths.get(i) + "', " + i + ")"); //NON-NLS
5566  }
5567  connection.commitTransaction();
5568  } catch (SQLException ex) {
5569  connection.rollbackTransaction();
5570  throw new TskCoreException("Error updating image paths.", ex);
5571  } finally {
5572  closeStatement(statement);
5573  connection.close();
5575  }
5576  }
5577 
5590  private List<AbstractFile> resultSetToAbstractFiles(ResultSet rs, CaseDbConnection connection) throws SQLException {
5591  ArrayList<AbstractFile> results = new ArrayList<AbstractFile>();
5592  try {
5593  while (rs.next()) {
5594  final short type = rs.getShort("type"); //NON-NLS
5595  if (type == TSK_DB_FILES_TYPE_ENUM.FS.getFileType()) {
5596  FsContent result;
5597  if (rs.getShort("meta_type") == TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()) { //NON-NLS
5598  result = directory(rs, null);
5599  } else {
5600  result = file(rs, null);
5601  }
5602  results.add(result);
5603  } else if (type == TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()) {
5604  final VirtualDirectory virtDir = virtualDirectory(rs);
5605  results.add(virtDir);
5606  } else if (type == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType()
5607  || type == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS.getFileType()
5608  || type == TSK_DB_FILES_TYPE_ENUM.CARVED.getFileType()) {
5610  String parentPath = rs.getString("parent_path"); //NON-NLS
5611  if (parentPath == null) {
5612  parentPath = "/"; //NON-NLS
5613  }
5614  LayoutFile lf = new LayoutFile(this,
5615  rs.getLong("obj_id"), //NON-NLS
5616  rs.getLong("data_source_obj_id"),
5617  rs.getString("name"), //NON-NLS
5618  atype,
5619  TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS
5620  TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), rs.getShort("meta_flags"), //NON-NLS
5621  rs.getLong("size"), //NON-NLS
5622  rs.getString("md5"), FileKnown.valueOf(rs.getByte("known")), parentPath, rs.getString("mime_type")); //NON-NLS
5623  results.add(lf);
5624  } else if (type == TSK_DB_FILES_TYPE_ENUM.DERIVED.getFileType()) {
5625  final DerivedFile df;
5626  df = derivedFile(rs, connection, AbstractContent.UNKNOWN_ID);
5627  results.add(df);
5628  } else if (type == TSK_DB_FILES_TYPE_ENUM.LOCAL.getFileType()) {
5629  final LocalFile lf;
5630  lf = localFile(rs, connection, AbstractContent.UNKNOWN_ID);
5631  results.add(lf);
5632  } else if (type == TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType()) {
5633  final SlackFile sf = slackFile(rs, null);
5634  results.add(sf);
5635  }
5636  } //end for each resultSet
5637  } catch (SQLException e) {
5638  logger.log(Level.SEVERE, "Error getting abstract files from result set", e); //NON-NLS
5639  }
5640 
5641  return results;
5642  }
5643 
5644  // This following methods generate AbstractFile objects from a ResultSet
5656  org.sleuthkit.datamodel.File file(ResultSet rs, FileSystem fs) throws SQLException {
5657  org.sleuthkit.datamodel.File f = new org.sleuthkit.datamodel.File(this, rs.getLong("obj_id"), //NON-NLS
5658  rs.getLong("data_source_obj_id"), rs.getLong("fs_obj_id"), //NON-NLS
5659  TskData.TSK_FS_ATTR_TYPE_ENUM.valueOf(rs.getShort("attr_type")), //NON-NLS
5660  rs.getInt("attr_id"), rs.getString("name"), rs.getLong("meta_addr"), rs.getInt("meta_seq"), //NON-NLS
5661  TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS
5662  TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS
5663  TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), //NON-NLS
5664  rs.getShort("meta_flags"), rs.getLong("size"), //NON-NLS
5665  rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
5666  (short) rs.getInt("mode"), rs.getInt("uid"), rs.getInt("gid"), //NON-NLS
5667  rs.getString("md5"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
5668  rs.getString("parent_path"), rs.getString("mime_type")); //NON-NLS
5669  f.setFileSystem(fs);
5670  return f;
5671  }
5672 
5684  Directory directory(ResultSet rs, FileSystem fs) throws SQLException {
5685  Directory dir = new Directory(this, rs.getLong("obj_id"), rs.getLong("data_source_obj_id"), rs.getLong("fs_obj_id"), //NON-NLS
5686  TskData.TSK_FS_ATTR_TYPE_ENUM.valueOf(rs.getShort("attr_type")), //NON-NLS
5687  rs.getInt("attr_id"), rs.getString("name"), rs.getLong("meta_addr"), rs.getInt("meta_seq"), //NON-NLS
5688  TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS
5689  TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS
5690  TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), //NON-NLS
5691  rs.getShort("meta_flags"), rs.getLong("size"), //NON-NLS
5692  rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
5693  rs.getShort("mode"), rs.getInt("uid"), rs.getInt("gid"), //NON-NLS
5694  rs.getString("md5"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
5695  rs.getString("parent_path")); //NON-NLS
5696  dir.setFileSystem(fs);
5697  return dir;
5698  }
5699 
5709  VirtualDirectory virtualDirectory(ResultSet rs) throws SQLException {
5710  String parentPath = rs.getString("parent_path"); //NON-NLS
5711  if (parentPath == null) {
5712  parentPath = "";
5713  }
5714  final VirtualDirectory vd = new VirtualDirectory(this, rs.getLong("obj_id"), //NON-NLS
5715  rs.getLong("data_source_obj_id"), rs.getString("name"), //NON-NLS
5716  TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS
5717  TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS
5718  TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), //NON-NLS
5719  rs.getShort("meta_flags"), rs.getString("md5"), //NON-NLS
5720  FileKnown.valueOf(rs.getByte("known")), parentPath); //NON-NLS
5721  return vd;
5722  }
5723 
5734  DerivedFile derivedFile(ResultSet rs, CaseDbConnection connection, long parentId) throws SQLException {
5735  boolean hasLocalPath = rs.getBoolean("has_path"); //NON-NLS
5736  long objId = rs.getLong("obj_id"); //NON-NLS
5737  String localPath = null;
5738  TskData.EncodingType encodingType = TskData.EncodingType.NONE;
5739  if (hasLocalPath) {
5740  ResultSet rsFilePath = null;
5741  try {
5742  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_LOCAL_PATH_AND_ENCODING_FOR_FILE);
5743  statement.clearParameters();
5744  statement.setLong(1, objId);
5745  rsFilePath = connection.executeQuery(statement);
5746  if (rsFilePath.next()) {
5747  localPath = rsFilePath.getString("path");
5748  encodingType = TskData.EncodingType.valueOf(rsFilePath.getInt("encoding_type"));
5749  }
5750  } catch (SQLException ex) {
5751  logger.log(Level.SEVERE, "Error getting encoding type for file " + objId, ex); //NON-NLS
5752  } finally {
5753  closeResultSet(rsFilePath);
5754  }
5755  }
5756  String parentPath = rs.getString("parent_path"); //NON-NLS
5757  if (parentPath == null) {
5758  parentPath = "";
5759  }
5760  final DerivedFile df = new DerivedFile(this, objId, rs.getLong("data_source_obj_id"),
5761  rs.getString("name"), //NON-NLS
5762  TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS
5763  TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS
5764  TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), rs.getShort("meta_flags"), //NON-NLS
5765  rs.getLong("size"), //NON-NLS
5766  rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
5767  rs.getString("md5"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
5768  parentPath, localPath, parentId, rs.getString("mime_type"),
5769  encodingType);
5770  return df;
5771  }
5772 
5784  LocalFile localFile(ResultSet rs, CaseDbConnection connection, long parentId) throws SQLException {
5785  long objId = rs.getLong("obj_id"); //NON-NLS
5786  String localPath = null;
5787  TskData.EncodingType encodingType = TskData.EncodingType.NONE;
5788  if (rs.getBoolean("has_path")) {
5789  ResultSet rsFilePath = null;
5790  try {
5791  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_LOCAL_PATH_AND_ENCODING_FOR_FILE);
5792  statement.clearParameters();
5793  statement.setLong(1, objId);
5794  rsFilePath = connection.executeQuery(statement);
5795  if (rsFilePath.next()) {
5796  localPath = rsFilePath.getString("path");
5797  encodingType = TskData.EncodingType.valueOf(rsFilePath.getInt("encoding_type"));
5798  }
5799  } catch (SQLException ex) {
5800  logger.log(Level.SEVERE, "Error getting encoding type for file " + objId, ex); //NON-NLS
5801  } finally {
5802  closeResultSet(rsFilePath);
5803  }
5804  }
5805  String parentPath = rs.getString("parent_path"); //NON-NLS
5806  if (null == parentPath) {
5807  parentPath = "";
5808  }
5809  LocalFile file = new LocalFile(this, objId, rs.getString("name"), //NON-NLS
5810  TSK_DB_FILES_TYPE_ENUM.valueOf(rs.getShort("type")), //NON-NLS
5811  TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS
5812  TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS
5813  TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), rs.getShort("meta_flags"), //NON-NLS
5814  rs.getLong("size"), //NON-NLS
5815  rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
5816  rs.getString("mime_type"), rs.getString("md5"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
5817  parentId, parentPath, rs.getLong("data_source_obj_id"),
5818  localPath, encodingType);
5819  return file;
5820  }
5821 
5833  org.sleuthkit.datamodel.SlackFile slackFile(ResultSet rs, FileSystem fs) throws SQLException {
5834  org.sleuthkit.datamodel.SlackFile f = new org.sleuthkit.datamodel.SlackFile(this, rs.getLong("obj_id"), //NON-NLS
5835  rs.getLong("data_source_obj_id"), rs.getLong("fs_obj_id"), //NON-NLS
5836  TskData.TSK_FS_ATTR_TYPE_ENUM.valueOf(rs.getShort("attr_type")), //NON-NLS
5837  rs.getInt("attr_id"), rs.getString("name"), rs.getLong("meta_addr"), rs.getInt("meta_seq"), //NON-NLS
5838  TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS
5839  TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS
5840  TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), //NON-NLS
5841  rs.getShort("meta_flags"), rs.getLong("size"), //NON-NLS
5842  rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS
5843  (short) rs.getInt("mode"), rs.getInt("uid"), rs.getInt("gid"), //NON-NLS
5844  rs.getString("md5"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS
5845  rs.getString("parent_path"), rs.getString("mime_type")); //NON-NLS
5846  f.setFileSystem(fs);
5847  return f;
5848  }
5849 
5861  List<Content> fileChildren(ResultSet rs, CaseDbConnection connection, long parentId) throws SQLException {
5862  List<Content> children = new ArrayList<Content>();
5863 
5864  while (rs.next()) {
5865  TskData.TSK_DB_FILES_TYPE_ENUM type = TskData.TSK_DB_FILES_TYPE_ENUM.valueOf(rs.getShort("type"));
5866 
5867  if (null != type) {
5868  switch (type) {
5869  case FS:
5870  FsContent result;
5871  if (rs.getShort("meta_type") == TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()) {
5872  result = directory(rs, null);
5873  } else {
5874  result = file(rs, null);
5875  }
5876  children.add(result);
5877  break;
5878  case VIRTUAL_DIR:
5879  VirtualDirectory virtDir = virtualDirectory(rs);
5880  children.add(virtDir);
5881  break;
5882  case UNALLOC_BLOCKS:
5883  case UNUSED_BLOCKS:
5884  case CARVED: {
5885  String parentPath = rs.getString("parent_path");
5886  if (parentPath == null) {
5887  parentPath = "";
5888  }
5889  final LayoutFile lf = new LayoutFile(this, rs.getLong("obj_id"),
5890  rs.getLong("data_source_obj_id"), rs.getString("name"), type,
5891  TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")),
5892  TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")),
5893  TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), rs.getShort("meta_flags"),
5894  rs.getLong("size"), rs.getString("md5"),
5895  FileKnown.valueOf(rs.getByte("known")), parentPath, rs.getString("mime_type"));
5896  children.add(lf);
5897  break;
5898  }
5899  case DERIVED:
5900  final DerivedFile df = derivedFile(rs, connection, parentId);
5901  children.add(df);
5902  break;
5903  case LOCAL: {
5904  final LocalFile lf = localFile(rs, connection, parentId);
5905  children.add(lf);
5906  break;
5907  }
5908  case SLACK: {
5909  final SlackFile sf = slackFile(rs, null);
5910  children.add(sf);
5911  break;
5912  }
5913  default:
5914  break;
5915  }
5916  }
5917  }
5918  return children;
5919  }
5920 
5942  public CaseDbQuery executeQuery(String query) throws TskCoreException {
5943  return new CaseDbQuery(query);
5944  }
5945 
5946  @Override
5947  protected void finalize() throws Throwable {
5948  try {
5949  close();
5950  } finally {
5951  super.finalize();
5952  }
5953  }
5954 
5958  public void close() {
5960  fileSystemIdMap.clear();
5961 
5962  try {
5963  connections.close();
5964  } catch (TskCoreException ex) {
5965  logger.log(Level.SEVERE, "Error closing database connection pool.", ex); //NON-NLS
5966  }
5967  try {
5968  if (this.caseHandle != null) {
5969  this.caseHandle.free();
5970  this.caseHandle = null;
5971  }
5972  } catch (TskCoreException ex) {
5973  logger.log(Level.SEVERE, "Error freeing case handle.", ex); //NON-NLS
5974  } finally {
5976  }
5977  }
5978 
5991  public boolean setKnown(AbstractFile file, FileKnown fileKnown) throws TskCoreException {
5992  long id = file.getId();
5993  FileKnown currentKnown = file.getKnown();
5994  if (currentKnown.compareTo(fileKnown) > 0) {
5995  return false;
5996  }
5997  CaseDbConnection connection = connections.getConnection();
5999  Statement statement = null;
6000  try {
6001  statement = connection.createStatement();
6002  connection.executeUpdate(statement, "UPDATE tsk_files " //NON-NLS
6003  + "SET known='" + fileKnown.getFileKnownValue() + "' " //NON-NLS
6004  + "WHERE obj_id=" + id); //NON-NLS
6005  file.setKnown(fileKnown);
6006  } catch (SQLException ex) {
6007  throw new TskCoreException("Error setting Known status.", ex);
6008  } finally {
6009  closeStatement(statement);
6010  connection.close();
6012  }
6013  return true;
6014  }
6015 
6025  public void setFileMIMEType(AbstractFile file, String mimeType) throws TskCoreException {
6026  CaseDbConnection connection = connections.getConnection();
6027  Statement statement = null;
6028  ResultSet rs = null;
6030  try {
6031  statement = connection.createStatement();
6032  connection.executeUpdate(statement, String.format("UPDATE tsk_files SET mime_type = '%s' WHERE obj_id = %d", mimeType, file.getId()));
6033  file.setMIMEType(mimeType);
6034  } catch (SQLException ex) {
6035  throw new TskCoreException(String.format("Error setting MIME type for file (obj_id = %s)", file.getId()), ex);
6036  } finally {
6037  closeResultSet(rs);
6038  closeStatement(statement);
6039  connection.close();
6041  }
6042  }
6043 
6053  void setMd5Hash(AbstractFile file, String md5Hash) throws TskCoreException {
6054  if (md5Hash == null) {
6055  return;
6056  }
6057  long id = file.getId();
6058  CaseDbConnection connection = connections.getConnection();
6060  try {
6061  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.UPDATE_FILE_MD5);
6062  statement.clearParameters();
6063  statement.setString(1, md5Hash.toLowerCase());
6064  statement.setLong(2, id);
6065  connection.executeUpdate(statement);
6066  file.setMd5Hash(md5Hash.toLowerCase());
6067  } catch (SQLException ex) {
6068  throw new TskCoreException("Error setting MD5 hash", ex);
6069  } finally {
6070  connection.close();
6072  }
6073  }
6074 
6086  if (newStatus == null) {
6087  return;
6088  }
6089  CaseDbConnection connection = connections.getConnection();
6091  Statement statement = null;
6092  try {
6093  statement = connection.createStatement();
6094  connection.executeUpdate(statement, "UPDATE blackboard_artifacts "
6095  + " SET review_status_id=" + newStatus.getID()
6096  + " WHERE blackboard_artifacts.artifact_id = " + artifact.getArtifactID());
6097  } catch (SQLException ex) {
6098  throw new TskCoreException("Error setting review status", ex);
6099  } finally {
6100  closeStatement(statement);
6101  connection.close();
6103  }
6104  }
6105 
6117  CaseDbConnection connection = connections.getConnection();
6119  Statement s = null;
6120  ResultSet rs = null;
6121  try {
6122  s = connection.createStatement();
6123  Short contentShort = contentType.getValue();
6124  rs = connection.executeQuery(s, "SELECT COUNT(*) AS count FROM tsk_files WHERE meta_type = '" + contentShort.toString() + "'"); //NON-NLS
6125  int count = 0;
6126  if (rs.next()) {
6127  count = rs.getInt("count");
6128  }
6129  return count;
6130  } catch (SQLException ex) {
6131  throw new TskCoreException("Error getting number of objects.", ex);
6132  } finally {
6133  closeResultSet(rs);
6134  closeStatement(s);
6135  connection.close();
6137  }
6138  }
6139 
6148  public static String escapeSingleQuotes(String text) {
6149  String escapedText = null;
6150  if (text != null) {
6151  escapedText = text.replaceAll("'", "''");
6152  }
6153  return escapedText;
6154  }
6155 
6163  public List<AbstractFile> findFilesByMd5(String md5Hash) {
6164  if (md5Hash == null) {
6165  return Collections.<AbstractFile>emptyList();
6166  }
6167  CaseDbConnection connection;
6168  try {
6169  connection = connections.getConnection();
6170  } catch (TskCoreException ex) {
6171  logger.log(Level.SEVERE, "Error finding files by md5 hash " + md5Hash, ex); //NON-NLS
6172  return Collections.<AbstractFile>emptyList();
6173  }
6175  Statement s = null;
6176  ResultSet rs = null;
6177  try {
6178  s = connection.createStatement();
6179  rs = connection.executeQuery(s, "SELECT * FROM tsk_files WHERE " //NON-NLS
6180  + " md5 = '" + md5Hash.toLowerCase() + "' " //NON-NLS
6181  + "AND size > 0"); //NON-NLS
6182  return resultSetToAbstractFiles(rs, connection);
6183  } catch (SQLException ex) {
6184  logger.log(Level.WARNING, "Error querying database.", ex); //NON-NLS
6185  return Collections.<AbstractFile>emptyList();
6186  } finally {
6187  closeResultSet(rs);
6188  closeStatement(s);
6189  connection.close();
6191  }
6192  }
6193 
6200  public boolean allFilesMd5Hashed() {
6201  CaseDbConnection connection;
6202  try {
6203  connection = connections.getConnection();
6204  } catch (TskCoreException ex) {
6205  logger.log(Level.SEVERE, "Error checking md5 hashing status", ex); //NON-NLS
6206  return false;
6207  }
6208  boolean allFilesAreHashed = false;
6210  Statement s = null;
6211  ResultSet rs = null;
6212  try {
6213  s = connection.createStatement();
6214  rs = connection.executeQuery(s, "SELECT COUNT(*) AS count FROM tsk_files " //NON-NLS
6215  + "WHERE dir_type = '" + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + "' " //NON-NLS
6216  + "AND md5 IS NULL " //NON-NLS
6217  + "AND size > '0'"); //NON-NLS
6218  if (rs.next() && rs.getInt("count") == 0) {
6219  allFilesAreHashed = true;
6220  }
6221  } catch (SQLException ex) {
6222  logger.log(Level.WARNING, "Failed to query whether all files have MD5 hashes", ex); //NON-NLS
6223  } finally {
6224  closeResultSet(rs);
6225  closeStatement(s);
6226  connection.close();
6228  }
6229  return allFilesAreHashed;
6230  }
6231 
6237  public int countFilesMd5Hashed() {
6238  CaseDbConnection connection;
6239  try {
6240  connection = connections.getConnection();
6241  } catch (TskCoreException ex) {
6242  logger.log(Level.SEVERE, "Error getting database connection for hashed files count", ex); //NON-NLS
6243  return 0;
6244  }
6245  int count = 0;
6247  Statement s = null;
6248  ResultSet rs = null;
6249  try {
6250  s = connection.createStatement();
6251  rs = connection.executeQuery(s, "SELECT COUNT(*) AS count FROM tsk_files " //NON-NLS
6252  + "WHERE md5 IS NOT NULL " //NON-NLS
6253  + "AND size > '0'"); //NON-NLS
6254  if (rs.next()) {
6255  count = rs.getInt("count");
6256  }
6257  } catch (SQLException ex) {
6258  logger.log(Level.WARNING, "Failed to query for all the files.", ex); //NON-NLS
6259  } finally {
6260  closeResultSet(rs);
6261  closeStatement(s);
6262  connection.close();
6264  }
6265  return count;
6266 
6267  }
6268 
6274  public void addErrorObserver(ErrorObserver observer) {
6275  sleuthkitCaseErrorObservers.add(observer);
6276  }
6277 
6283  public void removeErrorObserver(ErrorObserver observer) {
6284  int i = sleuthkitCaseErrorObservers.indexOf(observer);
6285  if (i >= 0) {
6286  sleuthkitCaseErrorObservers.remove(i);
6287  }
6288  }
6289 
6296  public void submitError(String context, String errorMessage) {
6297  for (ErrorObserver observer : sleuthkitCaseErrorObservers) {
6298  if (observer != null) {
6299  try {
6300  observer.receiveError(context, errorMessage);
6301  } catch (Exception ex) {
6302  logger.log(Level.SEVERE, "Observer client unable to receive message: {0}, {1}", new Object[]{context, errorMessage, ex});
6303  }
6304  }
6305  }
6306  }
6307 
6316  public List<TagName> getAllTagNames() throws TskCoreException {
6317  CaseDbConnection connection = connections.getConnection();
6319  ResultSet resultSet = null;
6320  try {
6321  // SELECT * FROM tag_names
6322  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_TAG_NAMES);
6323  resultSet = connection.executeQuery(statement);
6324  ArrayList<TagName> tagNames = new ArrayList<TagName>();
6325  while (resultSet.next()) {
6326  tagNames.add(new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")))); //NON-NLS
6327  }
6328  return tagNames;
6329  } catch (SQLException ex) {
6330  throw new TskCoreException("Error selecting rows from tag_names table", ex);
6331  } finally {
6332  closeResultSet(resultSet);
6333  connection.close();
6335  }
6336  }
6337 
6348  public List<TagName> getTagNamesInUse() throws TskCoreException {
6349  CaseDbConnection connection = connections.getConnection();
6351  ResultSet resultSet = null;
6352  try {
6353  // SELECT * FROM tag_names WHERE tag_name_id IN (SELECT tag_name_id from content_tags UNION SELECT tag_name_id FROM blackboard_artifact_tags)
6354  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_TAG_NAMES_IN_USE);
6355  resultSet = connection.executeQuery(statement);
6356  ArrayList<TagName> tagNames = new ArrayList<TagName>();
6357  while (resultSet.next()) {
6358  tagNames.add(new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")))); //NON-NLS
6359  }
6360  return tagNames;
6361  } catch (SQLException ex) {
6362  throw new TskCoreException("Error selecting rows from tag_names table", ex);
6363  } finally {
6364  closeResultSet(resultSet);
6365  connection.close();
6367  }
6368  }
6369 
6381  public TagName addTagName(String displayName, String description, TagName.HTML_COLOR color) throws TskCoreException {
6382  CaseDbConnection connection = connections.getConnection();
6384  ResultSet resultSet = null;
6385  try {
6386  // INSERT INTO tag_names (display_name, description, color) VALUES (?, ?, ?)
6387  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_TAG_NAME, Statement.RETURN_GENERATED_KEYS);
6388  statement.clearParameters();
6389  statement.setString(1, displayName);
6390  statement.setString(2, description);
6391  statement.setString(3, color.getName());
6392  connection.executeUpdate(statement);
6393  resultSet = statement.getGeneratedKeys();
6394  resultSet.next();
6395  return new TagName(resultSet.getLong(1), //last_insert_rowid()
6396  displayName, description, color);
6397  } catch (SQLException ex) {
6398  throw new TskCoreException("Error adding row for " + displayName + " tag name to tag_names table", ex);
6399  } finally {
6400  closeResultSet(resultSet);
6401  connection.close();
6403  }
6404  }
6405 
6419  public ContentTag addContentTag(Content content, TagName tagName, String comment, long beginByteOffset, long endByteOffset) throws TskCoreException {
6420  CaseDbConnection connection = connections.getConnection();
6422  ResultSet resultSet = null;
6423  try {
6424  // INSERT INTO content_tags (obj_id, tag_name_id, comment, begin_byte_offset, end_byte_offset) VALUES (?, ?, ?, ?, ?)
6425  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_CONTENT_TAG, Statement.RETURN_GENERATED_KEYS);
6426  statement.clearParameters();
6427  statement.setLong(1, content.getId());
6428  statement.setLong(2, tagName.getId());
6429  statement.setString(3, comment);
6430  statement.setLong(4, beginByteOffset);
6431  statement.setLong(5, endByteOffset);
6432  connection.executeUpdate(statement);
6433  resultSet = statement.getGeneratedKeys();
6434  resultSet.next();
6435  return new ContentTag(resultSet.getLong(1), //last_insert_rowid()
6436  content, tagName, comment, beginByteOffset, endByteOffset);
6437  } catch (SQLException ex) {
6438  throw new TskCoreException("Error adding row to content_tags table (obj_id = " + content.getId() + ", tag_name_id = " + tagName.getId() + ")", ex);
6439  } finally {
6440  closeResultSet(resultSet);
6441  connection.close();
6443  }
6444  }
6445 
6446  /*
6447  * Deletes a row from the content_tags table in the case database. @param
6448  * tag A ContentTag data transfer object (DTO) for the row to delete.
6449  * @throws TskCoreException
6450  */
6452  CaseDbConnection connection = connections.getConnection();
6454  try {
6455  // DELETE FROM content_tags WHERE tag_id = ?
6456  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.DELETE_CONTENT_TAG);
6457  statement.clearParameters();
6458  statement.setLong(1, tag.getId());
6459  connection.executeUpdate(statement);
6460  } catch (SQLException ex) {
6461  throw new TskCoreException("Error deleting row from content_tags table (id = " + tag.getId() + ")", ex);
6462  } finally {
6463  connection.close();
6465  }
6466  }
6467 
6476  public List<ContentTag> getAllContentTags() throws TskCoreException {
6477  CaseDbConnection connection = connections.getConnection();
6479  ResultSet resultSet = null;
6480  try {
6481  // SELECT * FROM content_tags INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id
6482  PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_CONTENT_TAGS);
6483