1 /* 2 * Sleuth Kit Data Model 3 * 4 * Copyright 2011-2019 Basis Technology Corp. 5 * Contact: carrier <at> sleuthkit <dot> org 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 package org.sleuthkit.datamodel; 20 21 import com.google.common.collect.ImmutableSet; 22 import com.google.common.eventbus.EventBus; 23 import com.mchange.v2.c3p0.ComboPooledDataSource; 24 import com.mchange.v2.c3p0.DataSources; 25 import com.mchange.v2.c3p0.PooledDataSource; 26 import com.zaxxer.sparsebits.SparseBitSet; 27 import java.beans.PropertyVetoException; 28 import java.io.BufferedInputStream; 29 import java.io.BufferedOutputStream; 30 import java.io.File; 31 import java.io.FileInputStream; 32 import java.io.FileOutputStream; 33 import java.io.IOException; 34 import java.io.InputStream; 35 import java.io.OutputStream; 36 import java.io.UnsupportedEncodingException; 37 import java.net.InetAddress; 38 import java.net.URLEncoder; 39 import java.nio.charset.StandardCharsets; 40 import java.nio.file.Paths; 41 import java.sql.Connection; 42 import java.sql.DriverManager; 43 import java.sql.PreparedStatement; 44 import java.sql.ResultSet; 45 import java.sql.SQLException; 46 import java.sql.Statement; 47 import java.text.SimpleDateFormat; 48 import java.util.ArrayList; 49 import java.util.Arrays; 50 import java.util.Collection; 51 import java.util.Collections; 52 import java.util.Date; 53 import java.util.EnumMap; 54 import java.util.HashMap; 55 import java.util.HashSet; 56 import java.util.LinkedHashMap; 57 import java.util.List; 58 import java.util.Map; 59 import java.util.MissingResourceException; 60 import java.util.ResourceBundle; 61 import java.util.Set; 62 import java.util.UUID; 63 import java.util.concurrent.ConcurrentHashMap; 64 import java.util.concurrent.locks.ReentrantReadWriteLock; 65 import java.util.logging.Level; 66 import java.util.logging.Logger; 67 import org.postgresql.util.PSQLState; 68 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; 69 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; 70 import org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE; 71 import org.sleuthkit.datamodel.IngestJobInfo.IngestJobStatusType; 72 import org.sleuthkit.datamodel.IngestModuleInfo.IngestModuleType; 73 import org.sleuthkit.datamodel.SleuthkitJNI.CaseDbHandle.AddImageProcess; 74 import org.sleuthkit.datamodel.TskData.DbType; 75 import org.sleuthkit.datamodel.TskData.FileKnown; 76 import org.sleuthkit.datamodel.TskData.ObjectType; 77 import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; 78 import org.sleuthkit.datamodel.TskData.TSK_FS_ATTR_TYPE_ENUM; 79 import org.sleuthkit.datamodel.TskData.TSK_FS_META_FLAG_ENUM; 80 import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM; 81 import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; 82 import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_TYPE_ENUM; 83 import org.sqlite.SQLiteConfig; 84 import org.sqlite.SQLiteDataSource; 85 import org.sqlite.SQLiteJDBCLoader; 86 87 /** 88 * Represents the case database with methods that provide abstractions for 89 * database operations. 90 */ 91 public class SleuthkitCase { 92 93 private static final int MAX_DB_NAME_LEN_BEFORE_TIMESTAMP = 47; 94 95 /** 96 * This must be the same as TSK_SCHEMA_VER and TSK_SCHEMA_MINOR_VER in 97 * tsk/auto/tsk_db.h. 98 */ 99 private static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION 100 = new CaseDbSchemaVersionNumber(8, 3); 101 102 private static final long BASE_ARTIFACT_ID = Long.MIN_VALUE; // Artifact ids will start at the lowest negative value 103 private static final Logger logger = Logger.getLogger(SleuthkitCase.class.getName()); 104 private static final ResourceBundle bundle = ResourceBundle.getBundle("org.sleuthkit.datamodel.Bundle"); 105 private static final int IS_REACHABLE_TIMEOUT_MS = 1000; 106 private static final String SQL_ERROR_CONNECTION_GROUP = "08"; 107 private static final String SQL_ERROR_AUTHENTICATION_GROUP = "28"; 108 private static final String SQL_ERROR_PRIVILEGE_GROUP = "42"; 109 private static final String SQL_ERROR_RESOURCE_GROUP = "53"; 110 private static final String SQL_ERROR_LIMIT_GROUP = "54"; 111 private static final String SQL_ERROR_INTERNAL_GROUP = "xx"; 112 private static final int MIN_USER_DEFINED_TYPE_ID = 10000; 113 114 private static final Set<String> CORE_TABLE_NAMES = ImmutableSet.of( 115 "tsk_events", 116 "tsk_event_descriptions", 117 "tsk_event_types", 118 "tsk_db_info", 119 "tsk_objects", 120 "tsk_image_info", 121 "tsk_image_names", 122 "tsk_vs_info", 123 "tsk_vs_parts", 124 "tsk_fs_info", 125 "tsk_file_layout", 126 "tsk_files", 127 "tsk_files_path", 128 "tsk_files_derived", 129 "tsk_files_derived_method", 130 "tag_names", 131 "content_tags", 132 "blackboard_artifact_tags", 133 "blackboard_artifacts", 134 "blackboard_attributes", 135 "blackboard_artifact_types", 136 "blackboard_attribute_types", 137 "data_source_info", 138 "file_encoding_types", 139 "ingest_module_types", 140 "ingest_job_status_types", 141 "ingest_modules", 142 "ingest_jobs", 143 "ingest_job_modules", 144 "account_types", 145 "accounts", 146 "account_relationships", 147 "review_statuses", 148 "reports,"); 149 150 private static final Set<String> CORE_INDEX_NAMES = ImmutableSet.of( 151 "parObjId", 152 "layout_objID", 153 "artifact_objID", 154 "artifact_artifact_objID", 155 "artifact_typeID", 156 "attrsArtifactID", 157 "mime_type", 158 "file_extension", 159 "relationships_account1", 160 "relationships_account2", 161 "relationships_relationship_source_obj_id", 162 "relationships_date_time", 163 "relationships_relationship_type", 164 "relationships_data_source_obj_id", 165 "events_time", 166 "events_type", 167 "events_data_source_obj_id", 168 "events_file_obj_id", 169 "events_artifact_id"); 170 171 private static final String TSK_VERSION_KEY = "TSK_VER"; 172 private static final String SCHEMA_MAJOR_VERSION_KEY = "SCHEMA_MAJOR_VERSION"; 173 private static final String SCHEMA_MINOR_VERSION_KEY = "SCHEMA_MINOR_VERSION"; 174 private static final String CREATION_SCHEMA_MAJOR_VERSION_KEY = "CREATION_SCHEMA_MAJOR_VERSION"; 175 private static final String CREATION_SCHEMA_MINOR_VERSION_KEY = "CREATION_SCHEMA_MINOR_VERSION"; 176 177 private final ConnectionPool connections; 178 private final Map<Long, VirtualDirectory> rootIdsToCarvedFileDirs = new HashMap<>(); 179 private final Map<Long, FileSystem> fileSystemIdMap = new HashMap<>(); // Cache for file system files. 180 private final List<ErrorObserver> sleuthkitCaseErrorObservers = new ArrayList<>(); 181 private final String databaseName; 182 private final String dbPath; 183 private final DbType dbType; 184 private final String caseDirPath; 185 private SleuthkitJNI.CaseDbHandle caseHandle; 186 private String dbBackupPath; 187 private Map<Integer, BlackboardArtifact.Type> typeIdToArtifactTypeMap; 188 private Map<Integer, BlackboardAttribute.Type> typeIdToAttributeTypeMap; 189 private Map<String, BlackboardArtifact.Type> typeNameToArtifactTypeMap; 190 private Map<String, BlackboardAttribute.Type> typeNameToAttributeTypeMap; 191 private CaseDbSchemaVersionNumber caseDBSchemaCreationVersion; 192 193 /* 194 * First parameter is used to specify the SparseBitSet to use, as object IDs 195 * can be larger than the max size of a SparseBitSet 196 */ 197 private final Map<Long, SparseBitSet> hasChildrenBitSetMap = new HashMap<>(); 198 199 private long nextArtifactId; // Used to ensure artifact ids come from the desired range. 200 // This read/write lock is used to implement a layer of locking on top of 201 // the locking protocol provided by the underlying SQLite database. The Java 202 // locking protocol improves performance for reasons that are not currently 203 // understood. Note that the lock is contructed to use a fairness policy. 204 private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true); 205 206 private CommunicationsManager communicationsMgr; 207 private TimelineManager timelineMgr; 208 private Blackboard blackboard; 209 private CaseDbAccessManager dbAccessManager; 210 211 private final Map<String, Set<Long>> deviceIdToDatasourceObjIdMap = new HashMap<>(); 212 213 private final EventBus eventBus = new EventBus("SleuthkitCase-EventBus"); 214 registerForEvents(Object listener)215 public void registerForEvents(Object listener) { 216 eventBus.register(listener); 217 } 218 unregisterForEvents(Object listener)219 public void unregisterForEvents(Object listener) { 220 eventBus.unregister(listener); 221 } 222 fireTSKEvent(Object event)223 void fireTSKEvent(Object event) { 224 eventBus.post(event); 225 } 226 227 // Cache of frequently used content objects (e.g. data source, file system). 228 private final Map<Long, Content> frequentlyUsedContentMap = new HashMap<>(); 229 230 private Examiner cachedCurrentExaminer = null; 231 232 /** 233 * Attempts to connect to the database with the passed in settings, throws 234 * if the settings are not sufficient to connect to the database type 235 * indicated. Only attempts to connect to remote databases. 236 * 237 * When issues occur, it attempts to diagnose them by looking at the 238 * exception messages, returning the appropriate user-facing text for the 239 * exception received. This method expects the Exceptions messages to be in 240 * English and compares against English text. 241 * 242 * @param info The connection information 243 * 244 * @throws org.sleuthkit.datamodel.TskCoreException 245 */ tryConnect(CaseDbConnectionInfo info)246 public static void tryConnect(CaseDbConnectionInfo info) throws TskCoreException { 247 // Check if we can talk to the database. 248 if (info.getHost() == null || info.getHost().isEmpty()) { 249 throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.MissingHostname")); //NON-NLS 250 } else if (info.getPort() == null || info.getPort().isEmpty()) { 251 throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.MissingPort")); //NON-NLS 252 } else if (info.getUserName() == null || info.getUserName().isEmpty()) { 253 throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.MissingUsername")); //NON-NLS 254 } else if (info.getPassword() == null || info.getPassword().isEmpty()) { 255 throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.MissingPassword")); //NON-NLS 256 } 257 258 try { 259 Class.forName("org.postgresql.Driver"); //NON-NLS 260 Connection conn = DriverManager.getConnection("jdbc:postgresql://" + info.getHost() + ":" + info.getPort() + "/postgres", info.getUserName(), info.getPassword()); //NON-NLS 261 if (conn != null) { 262 conn.close(); 263 } 264 } catch (SQLException ex) { 265 String result; 266 String sqlState = ex.getSQLState().toLowerCase(); 267 if (sqlState.startsWith(SQL_ERROR_CONNECTION_GROUP)) { 268 try { 269 if (InetAddress.getByName(info.getHost()).isReachable(IS_REACHABLE_TIMEOUT_MS)) { 270 // if we can reach the host, then it's probably port problem 271 result = bundle.getString("DatabaseConnectionCheck.Port"); //NON-NLS 272 } else { 273 result = bundle.getString("DatabaseConnectionCheck.HostnameOrPort"); //NON-NLS 274 } 275 } catch (IOException | MissingResourceException any) { 276 // it may be anything 277 result = bundle.getString("DatabaseConnectionCheck.Everything"); //NON-NLS 278 } 279 } else if (sqlState.startsWith(SQL_ERROR_AUTHENTICATION_GROUP)) { 280 result = bundle.getString("DatabaseConnectionCheck.Authentication"); //NON-NLS 281 } else if (sqlState.startsWith(SQL_ERROR_PRIVILEGE_GROUP)) { 282 result = bundle.getString("DatabaseConnectionCheck.Access"); //NON-NLS 283 } else if (sqlState.startsWith(SQL_ERROR_RESOURCE_GROUP)) { 284 result = bundle.getString("DatabaseConnectionCheck.ServerDiskSpace"); //NON-NLS 285 } else if (sqlState.startsWith(SQL_ERROR_LIMIT_GROUP)) { 286 result = bundle.getString("DatabaseConnectionCheck.ServerRestart"); //NON-NLS 287 } else if (sqlState.startsWith(SQL_ERROR_INTERNAL_GROUP)) { 288 result = bundle.getString("DatabaseConnectionCheck.InternalServerIssue"); //NON-NLS 289 } else { 290 result = bundle.getString("DatabaseConnectionCheck.Connection"); //NON-NLS 291 } 292 throw new TskCoreException(result); 293 } catch (ClassNotFoundException ex) { 294 throw new TskCoreException(bundle.getString("DatabaseConnectionCheck.Installation")); //NON-NLS 295 } 296 } 297 298 /** 299 * Private constructor, clients must use newCase() or openCase() method to 300 * create an instance of this class. 301 * 302 * @param dbPath The full path to a SQLite case database file. 303 * @param caseHandle A handle to a case database object in the native code 304 * SleuthKit layer. 305 * @param dbType The type of database we're dealing with 306 * 307 * @throws Exception 308 */ SleuthkitCase(String dbPath, SleuthkitJNI.CaseDbHandle caseHandle, DbType dbType)309 private SleuthkitCase(String dbPath, SleuthkitJNI.CaseDbHandle caseHandle, DbType dbType) throws Exception { 310 Class.forName("org.sqlite.JDBC"); 311 this.dbPath = dbPath; 312 this.dbType = dbType; 313 File dbFile = new File(dbPath); 314 this.caseDirPath = dbFile.getParentFile().getAbsolutePath(); 315 this.databaseName = dbFile.getName(); 316 this.connections = new SQLiteConnections(dbPath); 317 this.caseHandle = caseHandle; 318 init(); 319 logSQLiteJDBCDriverInfo(); 320 } 321 322 /** 323 * Private constructor, clients must use newCase() or openCase() method to 324 * create an instance of this class. 325 * 326 * @param host The PostgreSQL database server. 327 * @param port The port to use connect to the PostgreSQL database 328 * server. 329 * @param dbName The name of the case database. 330 * @param userName The user name to use to connect to the case database. 331 * @param password The password to use to connect to the case database. 332 * @param caseHandle A handle to a case database object in the native code 333 * @param dbType The type of database we're dealing with SleuthKit 334 * layer. 335 * @param caseDirPath The path to the root case directory. 336 * 337 * @throws Exception 338 */ SleuthkitCase(String host, int port, String dbName, String userName, String password, SleuthkitJNI.CaseDbHandle caseHandle, String caseDirPath, DbType dbType)339 private SleuthkitCase(String host, int port, String dbName, String userName, String password, SleuthkitJNI.CaseDbHandle caseHandle, String caseDirPath, DbType dbType) throws Exception { 340 this.dbPath = ""; 341 this.databaseName = dbName; 342 this.dbType = dbType; 343 this.caseDirPath = caseDirPath; 344 this.connections = new PostgreSQLConnections(host, port, dbName, userName, password); 345 this.caseHandle = caseHandle; 346 init(); 347 } 348 init()349 private void init() throws Exception { 350 typeIdToArtifactTypeMap = new ConcurrentHashMap<>(); 351 typeIdToAttributeTypeMap = new ConcurrentHashMap<>(); 352 typeNameToArtifactTypeMap = new ConcurrentHashMap<>(); 353 typeNameToAttributeTypeMap = new ConcurrentHashMap<>(); 354 355 /* 356 * The following methods need to be called before updateDatabaseSchema 357 * due to the way that updateFromSchema2toSchema3 was implemented. 358 */ 359 initBlackboardArtifactTypes(); 360 initBlackboardAttributeTypes(); 361 initNextArtifactId(); 362 updateDatabaseSchema(null); 363 364 try (CaseDbConnection connection = connections.getConnection()) { 365 initIngestModuleTypes(connection); 366 initIngestStatusTypes(connection); 367 initReviewStatuses(connection); 368 initEncodingTypes(connection); 369 populateHasChildrenMap(connection); 370 updateExaminers(connection); 371 initDBSchemaCreationVersion(connection); 372 } 373 374 blackboard = new Blackboard(this); 375 communicationsMgr = new CommunicationsManager(this); 376 timelineMgr = new TimelineManager(this); 377 dbAccessManager = new CaseDbAccessManager(this); 378 } 379 380 /** 381 * Returns a set of core table names in the SleuthKit Case database. 382 * 383 * @return set of core table names 384 */ getCoreTableNames()385 static Set<String> getCoreTableNames() { 386 return CORE_TABLE_NAMES; 387 } 388 389 /** 390 * Returns a set of core index names in the SleuthKit case database. 391 * 392 * @return set of core index names 393 */ getCoreIndexNames()394 static Set<String> getCoreIndexNames() { 395 return CORE_INDEX_NAMES; 396 } 397 398 /** 399 * Use the internal map to determine whether the content object has children 400 * (of any type). 401 * 402 * @param content 403 * 404 * @return true if the content has children, false otherwise 405 */ getHasChildren(Content content)406 boolean getHasChildren(Content content) { 407 long objId = content.getId(); 408 long mapIndex = objId / Integer.MAX_VALUE; 409 int mapValue = (int) (objId % Integer.MAX_VALUE); 410 411 synchronized (hasChildrenBitSetMap) { 412 if (hasChildrenBitSetMap.containsKey(mapIndex)) { 413 return hasChildrenBitSetMap.get(mapIndex).get(mapValue); 414 } 415 return false; 416 } 417 } 418 419 /** 420 * Add this objId to the list of objects that have children (of any type) 421 * 422 * @param objId 423 */ setHasChildren(Long objId)424 private void setHasChildren(Long objId) { 425 long mapIndex = objId / Integer.MAX_VALUE; 426 int mapValue = (int) (objId % Integer.MAX_VALUE); 427 428 synchronized (hasChildrenBitSetMap) { 429 if (hasChildrenBitSetMap.containsKey(mapIndex)) { 430 hasChildrenBitSetMap.get(mapIndex).set(mapValue); 431 } else { 432 SparseBitSet bitSet = new SparseBitSet(); 433 bitSet.set(mapValue); 434 hasChildrenBitSetMap.put(mapIndex, bitSet); 435 } 436 } 437 } 438 439 /** 440 * Gets the communications manager for this case. 441 * 442 * @return The per case CommunicationsManager object. 443 * 444 * @throws org.sleuthkit.datamodel.TskCoreException 445 */ getCommunicationsManager()446 public CommunicationsManager getCommunicationsManager() throws TskCoreException { 447 return communicationsMgr; 448 } 449 450 /** 451 * Gets the artifacts blackboard for this case. 452 * 453 * @return The per case Blackboard object. 454 */ getBlackboard()455 public Blackboard getBlackboard() { 456 return blackboard; 457 } 458 459 /** 460 * Gets the communications manager for this case. 461 * 462 * @return The per case TimelineManager object. 463 * 464 * @throws org.sleuthkit.datamodel.TskCoreException 465 */ getTimelineManager()466 public TimelineManager getTimelineManager() throws TskCoreException { 467 return timelineMgr; 468 } 469 470 /* 471 * Gets the case database access manager for this case. 472 * 473 * @return The per case CaseDbAccessManager object. 474 * 475 * @throws org.sleuthkit.datamodel.TskCoreException 476 */ getCaseDbAccessManager()477 public synchronized CaseDbAccessManager getCaseDbAccessManager() throws TskCoreException { 478 return dbAccessManager; 479 } 480 481 /** 482 * Make sure the predefined artifact types are in the artifact types table. 483 * 484 * @throws SQLException 485 * @throws TskCoreException 486 */ initBlackboardArtifactTypes()487 private void initBlackboardArtifactTypes() throws SQLException, TskCoreException { 488 CaseDbConnection connection = connections.getConnection(); 489 Statement statement = null; 490 ResultSet resultSet = null; 491 acquireSingleUserCaseWriteLock(); 492 try { 493 statement = connection.createStatement(); 494 for (ARTIFACT_TYPE type : ARTIFACT_TYPE.values()) { 495 try { 496 statement.execute("INSERT INTO blackboard_artifact_types (artifact_type_id, type_name, display_name) VALUES (" + type.getTypeID() + " , '" + type.getLabel() + "', '" + type.getDisplayName() + "')"); //NON-NLS 497 } catch (SQLException ex) { 498 resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM blackboard_artifact_types WHERE artifact_type_id = '" + type.getTypeID() + "'"); //NON-NLS 499 resultSet.next(); 500 if (resultSet.getLong("count") == 0) { 501 throw ex; 502 } 503 resultSet.close(); 504 resultSet = null; 505 } 506 this.typeIdToArtifactTypeMap.put(type.getTypeID(), new BlackboardArtifact.Type(type)); 507 this.typeNameToArtifactTypeMap.put(type.getLabel(), new BlackboardArtifact.Type(type)); 508 } 509 if (dbType == DbType.POSTGRESQL) { 510 int newPrimaryKeyIndex = Collections.max(Arrays.asList(ARTIFACT_TYPE.values())).getTypeID() + 1; 511 statement.execute("ALTER SEQUENCE blackboard_artifact_types_artifact_type_id_seq RESTART WITH " + newPrimaryKeyIndex); //NON-NLS 512 } 513 } finally { 514 closeResultSet(resultSet); 515 closeStatement(statement); 516 connection.close(); 517 releaseSingleUserCaseWriteLock(); 518 } 519 } 520 521 /** 522 * Make sure the predefined artifact attribute types are in the artifact 523 * attribute types table. 524 * 525 * @throws SQLException 526 * @throws TskCoreException 527 */ initBlackboardAttributeTypes()528 private void initBlackboardAttributeTypes() throws SQLException, TskCoreException { 529 CaseDbConnection connection = connections.getConnection(); 530 Statement statement = null; 531 ResultSet resultSet = null; 532 acquireSingleUserCaseWriteLock(); 533 try { 534 statement = connection.createStatement(); 535 for (ATTRIBUTE_TYPE type : ATTRIBUTE_TYPE.values()) { 536 try { 537 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 538 } catch (SQLException ex) { 539 resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM blackboard_attribute_types WHERE attribute_type_id = '" + type.getTypeID() + "'"); //NON-NLS 540 resultSet.next(); 541 if (resultSet.getLong("count") == 0) { 542 throw ex; 543 } 544 resultSet.close(); 545 resultSet = null; 546 } 547 this.typeIdToAttributeTypeMap.put(type.getTypeID(), new BlackboardAttribute.Type(type)); 548 this.typeNameToAttributeTypeMap.put(type.getLabel(), new BlackboardAttribute.Type(type)); 549 } 550 if (this.dbType == DbType.POSTGRESQL) { 551 int newPrimaryKeyIndex = Collections.max(Arrays.asList(ATTRIBUTE_TYPE.values())).getTypeID() + 1; 552 statement.execute("ALTER SEQUENCE blackboard_attribute_types_attribute_type_id_seq RESTART WITH " + newPrimaryKeyIndex); //NON-NLS 553 } 554 } finally { 555 closeResultSet(resultSet); 556 closeStatement(statement); 557 connection.close(); 558 releaseSingleUserCaseWriteLock(); 559 } 560 } 561 562 /** 563 * Initialize the next artifact id. If there are entries in the 564 * blackboard_artifacts table we will use max(artifact_id) + 1 otherwise we 565 * will initialize the value to 0x8000000000000000 (the maximum negative 566 * signed long). 567 * 568 * @throws SQLException 569 * @throws TskCoreException 570 */ initNextArtifactId()571 private void initNextArtifactId() throws SQLException, TskCoreException { 572 CaseDbConnection connection = connections.getConnection(); 573 Statement statement = null; 574 ResultSet resultSet = null; 575 acquireSingleUserCaseReadLock(); 576 try { 577 statement = connection.createStatement(); 578 resultSet = connection.executeQuery(statement, "SELECT MAX(artifact_id) AS max_artifact_id FROM blackboard_artifacts"); //NON-NLS 579 resultSet.next(); 580 this.nextArtifactId = resultSet.getLong("max_artifact_id") + 1; 581 if (this.nextArtifactId == 1) { 582 this.nextArtifactId = BASE_ARTIFACT_ID; 583 } 584 } finally { 585 closeResultSet(resultSet); 586 closeStatement(statement); 587 connection.close(); 588 releaseSingleUserCaseReadLock(); 589 } 590 } 591 592 /** 593 * Initialize ingest module types by adding them into the 594 * ingest_module_types database. 595 * 596 * @throws SQLException 597 * @throws TskCoreException 598 */ initIngestModuleTypes(CaseDbConnection connection)599 private void initIngestModuleTypes(CaseDbConnection connection) throws SQLException, TskCoreException { 600 Statement statement = null; 601 ResultSet resultSet = null; 602 acquireSingleUserCaseWriteLock(); 603 try { 604 statement = connection.createStatement(); 605 for (IngestModuleType type : IngestModuleType.values()) { 606 try { 607 statement.execute("INSERT INTO ingest_module_types (type_id, type_name) VALUES (" + type.ordinal() + ", '" + type.toString() + "');"); //NON-NLS 608 } catch (SQLException ex) { 609 resultSet = connection.executeQuery(statement, "SELECT COUNT(*) as count FROM ingest_module_types WHERE type_id = " + type.ordinal() + ";"); //NON-NLS 610 resultSet.next(); 611 if (resultSet.getLong("count") == 0) { 612 throw ex; 613 } 614 resultSet.close(); 615 resultSet = null; 616 } 617 } 618 } finally { 619 closeResultSet(resultSet); 620 closeStatement(statement); 621 releaseSingleUserCaseWriteLock(); 622 } 623 } 624 625 /** 626 * Initialize ingest status types by adding them into the 627 * ingest_job_status_types database. 628 * 629 * @throws SQLException 630 * @throws TskCoreException 631 */ initIngestStatusTypes(CaseDbConnection connection)632 private void initIngestStatusTypes(CaseDbConnection connection) throws SQLException, TskCoreException { 633 Statement statement = null; 634 ResultSet resultSet = null; 635 acquireSingleUserCaseWriteLock(); 636 try { 637 statement = connection.createStatement(); 638 for (IngestJobStatusType type : IngestJobStatusType.values()) { 639 try { 640 statement.execute("INSERT INTO ingest_job_status_types (type_id, type_name) VALUES (" + type.ordinal() + ", '" + type.toString() + "');"); //NON-NLS 641 } catch (SQLException ex) { 642 resultSet = connection.executeQuery(statement, "SELECT COUNT(*) as count FROM ingest_job_status_types WHERE type_id = " + type.ordinal() + ";"); //NON-NLS 643 resultSet.next(); 644 if (resultSet.getLong("count") == 0) { 645 throw ex; 646 } 647 resultSet.close(); 648 resultSet = null; 649 } 650 } 651 } finally { 652 closeResultSet(resultSet); 653 closeStatement(statement); 654 releaseSingleUserCaseWriteLock(); 655 } 656 } 657 658 /** 659 * Initialize the review statuses lookup table from the ReviewStatus enum. 660 * 661 * @throws SQLException 662 * @throws TskCoreException if there is an error initializing the table. 663 */ initReviewStatuses(CaseDbConnection connection)664 private void initReviewStatuses(CaseDbConnection connection) throws SQLException, TskCoreException { 665 Statement statement = null; 666 ResultSet resultSet = null; 667 acquireSingleUserCaseWriteLock(); 668 try { 669 statement = connection.createStatement(); 670 for (BlackboardArtifact.ReviewStatus status : BlackboardArtifact.ReviewStatus.values()) { 671 try { 672 statement.execute("INSERT INTO review_statuses (review_status_id, review_status_name, display_name) " //NON-NLS 673 + "VALUES (" + status.getID() + ",'" + status.getName() + "','" + status.getDisplayName() + "')"); //NON-NLS 674 } catch (SQLException ex) { 675 resultSet = connection.executeQuery(statement, "SELECT COUNT(*) as count FROM review_statuses WHERE review_status_id = " + status.getID()); //NON-NLS 676 resultSet.next(); 677 if (resultSet.getLong("count") == 0) { 678 throw ex; 679 } 680 resultSet.close(); 681 resultSet = null; 682 } 683 } 684 } finally { 685 closeResultSet(resultSet); 686 closeStatement(statement); 687 releaseSingleUserCaseWriteLock(); 688 } 689 } 690 691 /** 692 * Put the file encoding types into the table. This must be called after the 693 * database upgrades or the encoding_types table will not exist. 694 * 695 * @throws SQLException 696 * @throws TskCoreException 697 */ initEncodingTypes(CaseDbConnection connection)698 private void initEncodingTypes(CaseDbConnection connection) throws SQLException, TskCoreException { 699 Statement statement = null; 700 ResultSet resultSet = null; 701 acquireSingleUserCaseWriteLock(); 702 try { 703 statement = connection.createStatement(); 704 for (TskData.EncodingType type : TskData.EncodingType.values()) { 705 try { 706 statement.execute("INSERT INTO file_encoding_types (encoding_type, name) VALUES (" + type.getType() + " , '" + type.name() + "')"); //NON-NLS 707 } catch (SQLException ex) { 708 resultSet = connection.executeQuery(statement, "SELECT COUNT(*) as count FROM file_encoding_types WHERE encoding_type = " + type.getType()); //NON-NLS 709 resultSet.next(); 710 if (resultSet.getLong("count") == 0) { 711 throw ex; 712 } 713 resultSet.close(); 714 resultSet = null; 715 } 716 } 717 } finally { 718 closeResultSet(resultSet); 719 closeStatement(statement); 720 releaseSingleUserCaseWriteLock(); 721 } 722 } 723 724 /** 725 * Records the current examiner name in the tsk_examiners table 726 * 727 * @param CaseDbConnection 728 * 729 * @throws SQLException 730 * @throws TskCoreException 731 */ updateExaminers(CaseDbConnection connection)732 private void updateExaminers(CaseDbConnection connection) throws SQLException, TskCoreException { 733 734 String loginName = System.getProperty("user.name"); 735 if (loginName.isEmpty()) { 736 logger.log(Level.SEVERE, "Cannot determine logged in user name"); 737 return; 738 } 739 740 acquireSingleUserCaseWriteLock(); 741 Statement statement = connection.createStatement(); 742 try { 743 String query = "INTO tsk_examiners (login_name) VALUES ('" + loginName + "')"; 744 switch (getDatabaseType()) { 745 case POSTGRESQL: 746 query = "INSERT " + query + " ON CONFLICT DO NOTHING"; //NON-NLS 747 break; 748 case SQLITE: 749 query = "INSERT OR IGNORE " + query; 750 break; 751 default: 752 throw new TskCoreException("Unknown DB Type: " + getDatabaseType().name()); 753 } 754 755 statement.execute(query); //NON-NLS 756 } catch (SQLException ex) { 757 throw new TskCoreException("Error inserting row in tsk_examiners", ex); 758 } finally { 759 closeStatement(statement); 760 releaseSingleUserCaseWriteLock(); 761 } 762 } 763 764 /** 765 * Set up or update the hasChildren map using the tsk_objects table. 766 * 767 * @param connection 768 * 769 * @throws TskCoreException 770 */ populateHasChildrenMap(CaseDbConnection connection)771 private void populateHasChildrenMap(CaseDbConnection connection) throws TskCoreException { 772 long timestamp = System.currentTimeMillis(); 773 774 Statement statement = null; 775 ResultSet resultSet = null; 776 acquireSingleUserCaseWriteLock(); 777 try { 778 statement = connection.createStatement(); 779 resultSet = statement.executeQuery("select distinct par_obj_id from tsk_objects"); //NON-NLS 780 781 synchronized (hasChildrenBitSetMap) { 782 while (resultSet.next()) { 783 setHasChildren(resultSet.getLong("par_obj_id")); 784 } 785 } 786 long delay = System.currentTimeMillis() - timestamp; 787 logger.log(Level.INFO, "Time to initialize parent node cache: {0} ms", delay); //NON-NLS 788 } catch (SQLException ex) { 789 throw new TskCoreException("Error populating parent node cache", ex); 790 } finally { 791 closeResultSet(resultSet); 792 closeStatement(statement); 793 releaseSingleUserCaseWriteLock(); 794 } 795 } 796 797 /** 798 * Add the object IDs for a new data source to the has children map. At 799 * present, we simply reload the entire table. 800 * 801 * @throws TskCoreException 802 */ addDataSourceToHasChildrenMap()803 void addDataSourceToHasChildrenMap() throws TskCoreException { 804 805 CaseDbConnection connection = connections.getConnection(); 806 try { 807 populateHasChildrenMap(connection); 808 } finally { 809 if (connection != null) { 810 connection.close(); 811 } 812 } 813 } 814 815 /** 816 * Modify the case database to bring it up-to-date with the current version 817 * of the database schema. 818 * 819 * @param dbPath Path to the db file. If dbPath is null, no backup will be 820 * made. 821 * 822 * @throws Exception 823 */ updateDatabaseSchema(String dbPath)824 private void updateDatabaseSchema(String dbPath) throws Exception { 825 CaseDbConnection connection = connections.getConnection(); 826 ResultSet resultSet = null; 827 Statement statement = null; 828 acquireSingleUserCaseWriteLock(); 829 try { 830 connection.beginTransaction(); 831 832 boolean hasMinorVersion = false; 833 ResultSet columns = connection.getConnection().getMetaData().getColumns(null, null, "tsk_db_info", "schema%"); 834 while (columns.next()) { 835 if (columns.getString("COLUMN_NAME").equals("schema_minor_ver")) { 836 hasMinorVersion = true; 837 } 838 } 839 840 // Get the schema version number of the case database from the tsk_db_info table. 841 int dbSchemaMajorVersion; 842 int dbSchemaMinorVersion = 0; //schemas before 7 have no minor version , default it to zero. 843 844 statement = connection.createStatement(); 845 resultSet = connection.executeQuery(statement, "SELECT schema_ver" 846 + (hasMinorVersion ? ", schema_minor_ver" : "") 847 + " FROM tsk_db_info"); //NON-NLS 848 if (resultSet.next()) { 849 dbSchemaMajorVersion = resultSet.getInt("schema_ver"); //NON-NLS 850 if (hasMinorVersion) { 851 //if there is a minor version column, use it, else default to zero. 852 dbSchemaMinorVersion = resultSet.getInt("schema_minor_ver"); //NON-NLS 853 } 854 } else { 855 throw new TskCoreException(); 856 } 857 CaseDbSchemaVersionNumber dbSchemaVersion = new CaseDbSchemaVersionNumber(dbSchemaMajorVersion, dbSchemaMinorVersion); 858 859 resultSet.close(); 860 resultSet = null; 861 statement.close(); 862 statement = null; 863 //check schema compatibility 864 if (false == CURRENT_DB_SCHEMA_VERSION.isCompatible(dbSchemaVersion)) { 865 //we cannot open a db with a major schema version higher than the current one. 866 throw new TskUnsupportedSchemaVersionException( 867 "Unsupported DB schema version " + dbSchemaVersion + ", the highest supported schema version is " + CURRENT_DB_SCHEMA_VERSION.getMajor() + ".X"); 868 } else if (dbSchemaVersion.compareTo(CURRENT_DB_SCHEMA_VERSION) < 0) { 869 //The schema version is compatible,possibly after upgrades. 870 871 if (null != dbPath) { 872 // Make a backup copy of the database. Client code can get the path of the backup 873 // using the getBackupDatabasePath() method. 874 String backupFilePath = dbPath + ".schemaVer" + dbSchemaVersion.toString() + ".backup"; //NON-NLS 875 copyCaseDB(backupFilePath); 876 dbBackupPath = backupFilePath; 877 } 878 879 // ***CALL SCHEMA UPDATE METHODS HERE*** 880 // Each method should examine the schema version passed to it and either: 881 // a. do nothing and return the schema version unchanged, or 882 // b. upgrade the database and return the schema version that the db was upgraded to. 883 dbSchemaVersion = updateFromSchema2toSchema3(dbSchemaVersion, connection); 884 dbSchemaVersion = updateFromSchema3toSchema4(dbSchemaVersion, connection); 885 dbSchemaVersion = updateFromSchema4toSchema5(dbSchemaVersion, connection); 886 dbSchemaVersion = updateFromSchema5toSchema6(dbSchemaVersion, connection); 887 dbSchemaVersion = updateFromSchema6toSchema7(dbSchemaVersion, connection); 888 dbSchemaVersion = updateFromSchema7toSchema7dot1(dbSchemaVersion, connection); 889 dbSchemaVersion = updateFromSchema7dot1toSchema7dot2(dbSchemaVersion, connection); 890 dbSchemaVersion = updateFromSchema7dot2toSchema8dot0(dbSchemaVersion, connection); 891 dbSchemaVersion = updateFromSchema8dot0toSchema8dot1(dbSchemaVersion, connection); 892 dbSchemaVersion = updateFromSchema8dot1toSchema8dot2(dbSchemaVersion, connection); 893 dbSchemaVersion = updateFromSchema8dot2toSchema8dot3(dbSchemaVersion, connection); 894 statement = connection.createStatement(); 895 connection.executeUpdate(statement, "UPDATE tsk_db_info SET schema_ver = " + dbSchemaVersion.getMajor() + ", schema_minor_ver = " + dbSchemaVersion.getMinor()); //NON-NLS 896 connection.executeUpdate(statement, "UPDATE tsk_db_info_extended SET value = " + dbSchemaVersion.getMajor() + " WHERE name = '" + SCHEMA_MAJOR_VERSION_KEY + "'"); //NON-NLS 897 connection.executeUpdate(statement, "UPDATE tsk_db_info_extended SET value = " + dbSchemaVersion.getMinor() + " WHERE name = '" + SCHEMA_MINOR_VERSION_KEY + "'"); //NON-NLS 898 statement.close(); 899 statement = null; 900 } 901 902 connection.commitTransaction(); 903 } catch (Exception ex) { // Cannot do exception multi-catch in Java 6, so use catch-all. 904 connection.rollbackTransaction(); 905 throw ex; 906 } finally { 907 closeResultSet(resultSet); 908 closeStatement(statement); 909 connection.close(); 910 releaseSingleUserCaseWriteLock(); 911 } 912 } 913 914 /** 915 * Get the database schema creation version from database. This must be 916 * called after the database upgrades or the tsk_db_info_extended table may 917 * not exist. 918 * 919 * @throws SQLException 920 */ initDBSchemaCreationVersion(CaseDbConnection connection)921 private void initDBSchemaCreationVersion(CaseDbConnection connection) throws SQLException { 922 923 Statement statement = null; 924 ResultSet resultSet = null; 925 String createdSchemaMajorVersion = "0"; 926 String createdSchemaMinorVersion = "0"; 927 acquireSingleUserCaseReadLock(); 928 try { 929 statement = connection.createStatement(); 930 resultSet = connection.executeQuery(statement, "SELECT name, value FROM tsk_db_info_extended"); 931 while (resultSet.next()) { 932 String name = resultSet.getString("name"); 933 if (name.equals(CREATION_SCHEMA_MAJOR_VERSION_KEY) || name.equals("CREATED_SCHEMA_MAJOR_VERSION")) { 934 createdSchemaMajorVersion = resultSet.getString("value"); 935 } else if (name.equals(CREATION_SCHEMA_MINOR_VERSION_KEY) || name.equals("CREATED_SCHEMA_MINOR_VERSION")) { 936 createdSchemaMinorVersion = resultSet.getString("value"); 937 } 938 } 939 940 } finally { 941 closeResultSet(resultSet); 942 closeStatement(statement); 943 releaseSingleUserCaseReadLock(); 944 } 945 946 caseDBSchemaCreationVersion = new CaseDbSchemaVersionNumber(Integer.parseInt(createdSchemaMajorVersion), Integer.parseInt(createdSchemaMinorVersion)); 947 } 948 949 /** 950 * Make a duplicate / backup copy of the current case database. Makes a new 951 * copy only, and continues to use the current connection. 952 * 953 * @param newDBPath Path to the copy to be created. File will be overwritten 954 * if it exists. 955 * 956 * @throws IOException if copying fails. 957 */ copyCaseDB(String newDBPath)958 public void copyCaseDB(String newDBPath) throws IOException { 959 if (dbPath.isEmpty()) { 960 throw new IOException("Copying case database files is not supported for this type of case database"); //NON-NLS 961 } 962 InputStream in = null; 963 OutputStream out = null; 964 acquireSingleUserCaseWriteLock(); 965 try { 966 InputStream inFile = new FileInputStream(dbPath); 967 in = new BufferedInputStream(inFile); 968 OutputStream outFile = new FileOutputStream(newDBPath); 969 out = new BufferedOutputStream(outFile); 970 int bytesRead = in.read(); 971 while (bytesRead != -1) { 972 out.write(bytesRead); 973 bytesRead = in.read(); 974 } 975 } finally { 976 try { 977 if (in != null) { 978 in.close(); 979 } 980 if (out != null) { 981 out.flush(); 982 out.close(); 983 } 984 } catch (IOException e) { 985 logger.log(Level.WARNING, "Could not close streams after db copy", e); //NON-NLS 986 } 987 releaseSingleUserCaseWriteLock(); 988 } 989 } 990 991 /** 992 * Write some SQLite JDBC driver details to the log file. 993 */ logSQLiteJDBCDriverInfo()994 private void logSQLiteJDBCDriverInfo() { 995 try { 996 SleuthkitCase.logger.info(String.format("sqlite-jdbc version %s loaded in %s mode", //NON-NLS 997 SQLiteJDBCLoader.getVersion(), SQLiteJDBCLoader.isNativeMode() 998 ? "native" : "pure-java")); //NON-NLS 999 } catch (Exception ex) { 1000 SleuthkitCase.logger.log(Level.SEVERE, "Error querying case database mode", ex); 1001 } 1002 } 1003 1004 /** 1005 * Updates a schema version 2 database to a schema version 3 database. 1006 * 1007 * @param schemaVersion The current schema version of the database. 1008 * @param connection A connection to the case database. 1009 * 1010 * @return The new database schema version. 1011 * 1012 * @throws SQLException If there is an error completing a database 1013 * operation. 1014 * @throws TskCoreException If there is an error completing a database 1015 * operation via another SleuthkitCase method. 1016 */ 1017 @SuppressWarnings("deprecation") updateFromSchema2toSchema3(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection)1018 private CaseDbSchemaVersionNumber updateFromSchema2toSchema3(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { 1019 if (schemaVersion.getMajor() != 2) { 1020 return schemaVersion; 1021 } 1022 Statement statement = null; 1023 Statement updateStatement = null; 1024 ResultSet resultSet = null; 1025 acquireSingleUserCaseWriteLock(); 1026 try { 1027 statement = connection.createStatement(); 1028 1029 // Add new tables for tags. 1030 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 1031 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 1032 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 1033 1034 // Add a new table for reports. 1035 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 1036 1037 // Add new columns to the image info table. 1038 statement.execute("ALTER TABLE tsk_image_info ADD COLUMN size INTEGER;"); //NON-NLS 1039 statement.execute("ALTER TABLE tsk_image_info ADD COLUMN md5 TEXT;"); //NON-NLS 1040 statement.execute("ALTER TABLE tsk_image_info ADD COLUMN display_name TEXT;"); //NON-NLS 1041 1042 // Add a new column to the file system info table. 1043 statement.execute("ALTER TABLE tsk_fs_info ADD COLUMN display_name TEXT;"); //NON-NLS 1044 1045 // Add a new column to the file table. 1046 statement.execute("ALTER TABLE tsk_files ADD COLUMN meta_seq INTEGER;"); //NON-NLS 1047 1048 // Add new columns and indexes to the attributes table and populate the 1049 // new column. Note that addition of the new column is a denormalization 1050 // to optimize attribute queries. 1051 statement.execute("ALTER TABLE blackboard_attributes ADD COLUMN artifact_type_id INTEGER NULL NOT NULL DEFAULT -1;"); //NON-NLS 1052 statement.execute("CREATE INDEX attribute_artifactTypeId ON blackboard_attributes(artifact_type_id);"); //NON-NLS 1053 statement.execute("CREATE INDEX attribute_valueText ON blackboard_attributes(value_text);"); //NON-NLS 1054 statement.execute("CREATE INDEX attribute_valueInt32 ON blackboard_attributes(value_int32);"); //NON-NLS 1055 statement.execute("CREATE INDEX attribute_valueInt64 ON blackboard_attributes(value_int64);"); //NON-NLS 1056 statement.execute("CREATE INDEX attribute_valueDouble ON blackboard_attributes(value_double);"); //NON-NLS 1057 resultSet = statement.executeQuery("SELECT attrs.artifact_id AS artifact_id, " //NON-NLS 1058 + "arts.artifact_type_id AS artifact_type_id " //NON-NLS 1059 + "FROM blackboard_attributes AS attrs " //NON-NLS 1060 + "INNER JOIN blackboard_artifacts AS arts " //NON-NLS 1061 + "WHERE attrs.artifact_id = arts.artifact_id;"); //NON-NLS 1062 updateStatement = connection.createStatement(); 1063 while (resultSet.next()) { 1064 long artifactId = resultSet.getLong("artifact_id"); 1065 int artifactTypeId = resultSet.getInt("artifact_type_id"); 1066 updateStatement.executeUpdate( 1067 "UPDATE blackboard_attributes " //NON-NLS 1068 + "SET artifact_type_id = " + artifactTypeId //NON-NLS 1069 + " WHERE blackboard_attributes.artifact_id = " + artifactId + ";"); //NON-NLS 1070 } 1071 resultSet.close(); 1072 resultSet = null; 1073 1074 // Convert existing tag artifact and attribute rows to rows in the new tags tables. 1075 // TODO: This code depends on prepared statements that could evolve with 1076 // time, breaking this upgrade. The code that follows should be rewritten 1077 // to do everything with SQL specific to case database schema version 2. 1078 HashMap<String, TagName> tagNames = new HashMap<String, TagName>(); 1079 for (BlackboardArtifact artifact : getBlackboardArtifacts(ARTIFACT_TYPE.TSK_TAG_FILE)) { 1080 Content content = getContentById(artifact.getObjectID()); 1081 String name = ""; //NON-NLS 1082 String comment = ""; //NON-NLS 1083 ArrayList<BlackboardAttribute> attributes = getBlackboardAttributes(artifact); 1084 for (BlackboardAttribute attribute : attributes) { 1085 if (attribute.getAttributeTypeID() == ATTRIBUTE_TYPE.TSK_TAG_NAME.getTypeID()) { 1086 name = attribute.getValueString(); 1087 } else if (attribute.getAttributeTypeID() == ATTRIBUTE_TYPE.TSK_COMMENT.getTypeID()) { 1088 comment = attribute.getValueString(); 1089 } 1090 } 1091 if (!name.isEmpty()) { 1092 TagName tagName; 1093 if (tagNames.containsKey(name)) { 1094 tagName = tagNames.get(name); 1095 } else { 1096 tagName = addTagName(name, "", TagName.HTML_COLOR.NONE); //NON-NLS 1097 tagNames.put(name, tagName); 1098 } 1099 addContentTag(content, tagName, comment, 0, content.getSize() - 1); 1100 } 1101 } 1102 for (BlackboardArtifact artifact : getBlackboardArtifacts(ARTIFACT_TYPE.TSK_TAG_ARTIFACT)) { 1103 long taggedArtifactId = -1; 1104 String name = ""; //NON-NLS 1105 String comment = ""; //NON-NLS 1106 ArrayList<BlackboardAttribute> attributes = getBlackboardAttributes(artifact); 1107 for (BlackboardAttribute attribute : attributes) { 1108 if (attribute.getAttributeTypeID() == ATTRIBUTE_TYPE.TSK_TAG_NAME.getTypeID()) { 1109 name = attribute.getValueString(); 1110 } else if (attribute.getAttributeTypeID() == ATTRIBUTE_TYPE.TSK_COMMENT.getTypeID()) { 1111 comment = attribute.getValueString(); 1112 } else if (attribute.getAttributeTypeID() == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()) { 1113 taggedArtifactId = attribute.getValueLong(); 1114 } 1115 } 1116 if (taggedArtifactId != -1 && !name.isEmpty()) { 1117 TagName tagName; 1118 if (tagNames.containsKey(name)) { 1119 tagName = tagNames.get(name); 1120 } else { 1121 tagName = addTagName(name, "", TagName.HTML_COLOR.NONE); //NON-NLS 1122 tagNames.put(name, tagName); 1123 } 1124 addBlackboardArtifactTag(getBlackboardArtifact(taggedArtifactId), tagName, comment); 1125 } 1126 } 1127 statement.execute( 1128 "DELETE FROM blackboard_attributes WHERE artifact_id IN " //NON-NLS 1129 + "(SELECT artifact_id FROM blackboard_artifacts WHERE artifact_type_id = " //NON-NLS 1130 + ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() 1131 + " OR artifact_type_id = " + ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID() + ");"); //NON-NLS 1132 statement.execute( 1133 "DELETE FROM blackboard_artifacts WHERE artifact_type_id = " //NON-NLS 1134 + ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() 1135 + " OR artifact_type_id = " + ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID() + ";"); //NON-NLS 1136 1137 return new CaseDbSchemaVersionNumber(3, 0); 1138 } finally { 1139 closeStatement(updateStatement); 1140 closeResultSet(resultSet); 1141 closeStatement(statement); 1142 connection.close(); 1143 releaseSingleUserCaseWriteLock(); 1144 } 1145 } 1146 1147 /** 1148 * Updates a schema version 3 database to a schema version 4 database. 1149 * 1150 * @param schemaVersion The current schema version of the database. 1151 * @param connection A connection to the case database. 1152 * 1153 * @return The new database schema version. 1154 * 1155 * @throws SQLException If there is an error completing a database 1156 * operation. 1157 * @throws TskCoreException If there is an error completing a database 1158 * operation via another SleuthkitCase method. 1159 */ updateFromSchema3toSchema4(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection)1160 private CaseDbSchemaVersionNumber updateFromSchema3toSchema4(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { 1161 if (schemaVersion.getMajor() != 3) { 1162 return schemaVersion; 1163 } 1164 1165 Statement statement = null; 1166 ResultSet resultSet = null; 1167 Statement queryStatement = null; 1168 ResultSet queryResultSet = null; 1169 Statement updateStatement = null; 1170 acquireSingleUserCaseWriteLock(); 1171 try { 1172 // Add mime_type column to tsk_files table. Populate with general 1173 // info artifact file signature data. 1174 statement = connection.createStatement(); 1175 updateStatement = connection.createStatement(); 1176 statement.execute("ALTER TABLE tsk_files ADD COLUMN mime_type TEXT;"); 1177 resultSet = statement.executeQuery("SELECT files.obj_id AS obj_id, attrs.value_text AS value_text " 1178 + "FROM tsk_files AS files, blackboard_attributes AS attrs, blackboard_artifacts AS arts " 1179 + "WHERE files.obj_id = arts.obj_id AND " 1180 + "arts.artifact_id = attrs.artifact_id AND " 1181 + "arts.artifact_type_id = 1 AND " 1182 + "attrs.attribute_type_id = 62"); 1183 while (resultSet.next()) { 1184 updateStatement.executeUpdate( 1185 "UPDATE tsk_files " //NON-NLS 1186 + "SET mime_type = '" + resultSet.getString("value_text") + "' " //NON-NLS 1187 + "WHERE tsk_files.obj_id = " + resultSet.getInt("obj_id") + ";"); //NON-NLS 1188 } 1189 resultSet.close(); 1190 1191 // Add value_type column to blackboard_attribute_types table. 1192 statement.execute("ALTER TABLE blackboard_attribute_types ADD COLUMN value_type INTEGER NOT NULL DEFAULT -1;"); 1193 resultSet = statement.executeQuery("SELECT * FROM blackboard_attribute_types AS types"); //NON-NLS 1194 while (resultSet.next()) { 1195 int attributeTypeId = resultSet.getInt("attribute_type_id"); 1196 String attributeLabel = resultSet.getString("type_name"); 1197 if (attributeTypeId < MIN_USER_DEFINED_TYPE_ID) { 1198 updateStatement.executeUpdate( 1199 "UPDATE blackboard_attribute_types " //NON-NLS 1200 + "SET value_type = " + ATTRIBUTE_TYPE.fromLabel(attributeLabel).getValueType().getType() + " " //NON-NLS 1201 + "WHERE blackboard_attribute_types.attribute_type_id = " + attributeTypeId + ";"); //NON-NLS 1202 } 1203 } 1204 resultSet.close(); 1205 1206 // Add a data_sources_info table. 1207 queryStatement = connection.createStatement(); 1208 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));"); 1209 resultSet = statement.executeQuery("SELECT * FROM tsk_objects WHERE par_obj_id IS NULL"); 1210 while (resultSet.next()) { 1211 long objectId = resultSet.getLong("obj_id"); 1212 String timeZone = ""; 1213 queryResultSet = queryStatement.executeQuery("SELECT tzone FROM tsk_image_info WHERE obj_id = " + objectId); 1214 if (queryResultSet.next()) { 1215 timeZone = queryResultSet.getString("tzone"); 1216 } 1217 queryResultSet.close(); 1218 updateStatement.executeUpdate("INSERT INTO data_source_info (obj_id, device_id, time_zone) " 1219 + "VALUES(" + objectId + ", '" + UUID.randomUUID().toString() + "' , '" + timeZone + "');"); 1220 } 1221 resultSet.close(); 1222 1223 // Add data_source_obj_id column to the tsk_files table. 1224 // 1225 // NOTE: A new case database will have the following FK constraint: 1226 // 1227 // REFERENCES data_source_info (obj_id) 1228 // 1229 // The constraint is sacrificed here to avoid having to create and 1230 // populate a new tsk_files table. 1231 // 1232 // TODO: Do this right. 1233 statement.execute("ALTER TABLE tsk_files ADD COLUMN data_source_obj_id BIGINT NOT NULL DEFAULT -1;"); 1234 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"); 1235 while (resultSet.next()) { 1236 long fileId = resultSet.getLong("obj_id"); 1237 long dataSourceId = getDataSourceObjectId(connection, fileId); 1238 updateStatement.executeUpdate("UPDATE tsk_files SET data_source_obj_id = " + dataSourceId + " WHERE obj_id = " + fileId + ";"); 1239 } 1240 resultSet.close(); 1241 statement.execute("CREATE TABLE ingest_module_types (type_id INTEGER PRIMARY KEY, type_name TEXT NOT NULL)"); //NON-NLS 1242 statement.execute("CREATE TABLE ingest_job_status_types (type_id INTEGER PRIMARY KEY, type_name TEXT NOT NULL)"); //NON-NLS 1243 if (this.dbType.equals(DbType.SQLITE)) { 1244 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 1245 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 1246 } else { 1247 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 1248 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 1249 } 1250 1251 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 1252 initIngestModuleTypes(connection); 1253 initIngestStatusTypes(connection); 1254 1255 return new CaseDbSchemaVersionNumber(4, 0); 1256 1257 } finally { 1258 closeResultSet(queryResultSet); 1259 closeStatement(queryStatement); 1260 closeStatement(updateStatement); 1261 closeResultSet(resultSet); 1262 closeStatement(statement); 1263 releaseSingleUserCaseWriteLock(); 1264 } 1265 } 1266 1267 /** 1268 * Updates a schema version 4 database to a schema version 5 database. 1269 * 1270 * @param schemaVersion The current schema version of the database. 1271 * @param connection A connection to the case database. 1272 * 1273 * @return The new database schema version. 1274 * 1275 * @throws SQLException If there is an error completing a database 1276 * operation. 1277 * @throws TskCoreException If there is an error completing a database 1278 * operation via another SleuthkitCase method. 1279 */ updateFromSchema4toSchema5(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection)1280 private CaseDbSchemaVersionNumber updateFromSchema4toSchema5(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { 1281 if (schemaVersion.getMajor() != 4) { 1282 return schemaVersion; 1283 } 1284 1285 Statement statement = null; 1286 acquireSingleUserCaseWriteLock(); 1287 try { 1288 // Add the review_statuses lookup table. 1289 statement = connection.createStatement(); 1290 statement.execute("CREATE TABLE review_statuses (review_status_id INTEGER PRIMARY KEY, review_status_name TEXT NOT NULL, display_name TEXT NOT NULL)"); 1291 1292 /* 1293 * Add review_status_id column to artifacts table. 1294 * 1295 * NOTE: For DBs created with schema 5 we define a foreign key 1296 * constraint on the review_status_column. We don't bother with this 1297 * for DBs updated to schema 5 because of limitations of the SQLite 1298 * ALTER TABLE command. 1299 */ 1300 statement.execute("ALTER TABLE blackboard_artifacts ADD COLUMN review_status_id INTEGER NOT NULL DEFAULT " + BlackboardArtifact.ReviewStatus.UNDECIDED.getID()); 1301 1302 // Add the encoding table 1303 statement.execute("CREATE TABLE file_encoding_types (encoding_type INTEGER PRIMARY KEY, name TEXT NOT NULL);"); 1304 initEncodingTypes(connection); 1305 1306 /* 1307 * This needs to be done due to a Autopsy/TSK out of synch problem. 1308 * Without this, it is possible to upgrade from version 4 to 5 and 1309 * then 5 to 6, but not from 4 to 6. 1310 */ 1311 initReviewStatuses(connection); 1312 1313 // Add encoding type column to tsk_files_path 1314 // This should really have the FOREIGN KEY constraint but there are problems 1315 // getting that to work, so we don't add it on this upgrade path. 1316 statement.execute("ALTER TABLE tsk_files_path ADD COLUMN encoding_type INTEGER NOT NULL DEFAULT 0;"); 1317 1318 return new CaseDbSchemaVersionNumber(5, 0); 1319 1320 } finally { 1321 closeStatement(statement); 1322 releaseSingleUserCaseWriteLock(); 1323 } 1324 } 1325 1326 /** 1327 * Updates a schema version 5 database to a schema version 6 database. 1328 * 1329 * @param schemaVersion The current schema version of the database. 1330 * @param connection A connection to the case database. 1331 * 1332 * @return The new database schema version. 1333 * 1334 * @throws SQLException If there is an error completing a database 1335 * operation. 1336 * @throws TskCoreException If there is an error completing a database 1337 * operation via another SleuthkitCase method. 1338 */ updateFromSchema5toSchema6(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection)1339 private CaseDbSchemaVersionNumber updateFromSchema5toSchema6(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { 1340 if (schemaVersion.getMajor() != 5) { 1341 return schemaVersion; 1342 } 1343 1344 /* 1345 * This upgrade fixes a bug where some releases had artifact review 1346 * status support in the case database and others did not. 1347 */ 1348 Statement statement = null; 1349 ResultSet resultSet = null; 1350 acquireSingleUserCaseWriteLock(); 1351 try { 1352 /* 1353 * Add the review_statuses lookup table, if missing. 1354 */ 1355 statement = connection.createStatement(); 1356 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)"); 1357 1358 resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM review_statuses"); //NON-NLS 1359 resultSet.next(); 1360 if (resultSet.getLong("count") == 0) { 1361 /* 1362 * Add review_status_id column to artifacts table. 1363 * 1364 * NOTE: For DBs created with schema 5 or 6 we define a foreign 1365 * key constraint on the review_status_column. We don't bother 1366 * with this for DBs updated to schema 5 or 6 because of 1367 * limitations of the SQLite ALTER TABLE command. 1368 */ 1369 statement.execute("ALTER TABLE blackboard_artifacts ADD COLUMN review_status_id INTEGER NOT NULL DEFAULT " + BlackboardArtifact.ReviewStatus.UNDECIDED.getID()); 1370 } 1371 1372 return new CaseDbSchemaVersionNumber(6, 0); 1373 1374 } finally { 1375 closeResultSet(resultSet); 1376 closeStatement(statement); 1377 releaseSingleUserCaseWriteLock(); 1378 } 1379 } 1380 1381 /** 1382 * Updates a schema version 6 database to a schema version 7 database. 1383 * 1384 * @param schemaVersion The current schema version of the database. 1385 * @param connection A connection to the case database. 1386 * 1387 * @return The new database schema version. 1388 * 1389 * @throws SQLException If there is an error completing a database 1390 * operation. 1391 * @throws TskCoreException If there is an error completing a database 1392 * operation via another SleuthkitCase method. 1393 */ updateFromSchema6toSchema7(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection)1394 private CaseDbSchemaVersionNumber updateFromSchema6toSchema7(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { 1395 if (schemaVersion.getMajor() != 6) { 1396 return schemaVersion; 1397 } 1398 1399 /* 1400 * This upgrade adds an indexed extension column to the tsk_files table. 1401 */ 1402 Statement statement = null; 1403 Statement updstatement = null; 1404 ResultSet resultSet = null; 1405 acquireSingleUserCaseWriteLock(); 1406 try { 1407 statement = connection.createStatement(); 1408 updstatement = connection.createStatement(); 1409 statement.execute("ALTER TABLE tsk_files ADD COLUMN extension TEXT"); 1410 1411 resultSet = connection.executeQuery(statement, "SELECT obj_id,name FROM tsk_files"); //NON-NLS 1412 while (resultSet.next()) { 1413 long objID = resultSet.getLong("obj_id"); 1414 String name = resultSet.getString("name"); 1415 updstatement.executeUpdate("UPDATE tsk_files SET extension = '" + escapeSingleQuotes(extractExtension(name)) + "' " 1416 + "WHERE obj_id = " + objID); 1417 } 1418 1419 statement.execute("CREATE INDEX file_extension ON tsk_files ( extension )"); 1420 1421 // Add artifact_obj_id column to blackboard_artifacts table, data conversion for old versions isn't necesarry. 1422 statement.execute("ALTER TABLE blackboard_artifacts ADD COLUMN artifact_obj_id INTEGER NOT NULL DEFAULT -1"); 1423 1424 return new CaseDbSchemaVersionNumber(7, 0); 1425 1426 } finally { 1427 closeResultSet(resultSet); 1428 closeStatement(statement); 1429 closeStatement(updstatement); 1430 releaseSingleUserCaseWriteLock(); 1431 } 1432 } 1433 1434 /** 1435 * Updates a schema version 7 database to a schema version 7.1 database. 1436 * 1437 * @param schemaVersion The current schema version of the database. 1438 * @param connection A connection to the case database. 1439 * 1440 * @return The new database schema version. 1441 * 1442 * @throws SQLException If there is an error completing a database 1443 * operation. 1444 * @throws TskCoreException If there is an error completing a database 1445 * operation via another SleuthkitCase method. 1446 */ updateFromSchema7toSchema7dot1(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection)1447 private CaseDbSchemaVersionNumber updateFromSchema7toSchema7dot1(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { 1448 if (schemaVersion.getMajor() != 7) { 1449 return schemaVersion; 1450 } 1451 1452 if (schemaVersion.getMinor() != 0) { 1453 return schemaVersion; 1454 } 1455 1456 /* 1457 * This upgrade adds a minor version number column. 1458 */ 1459 Statement statement = null; 1460 ResultSet resultSet = null; 1461 acquireSingleUserCaseWriteLock(); 1462 try { 1463 statement = connection.createStatement(); 1464 1465 //add the schema minor version number column. 1466 if (schemaVersion.getMinor() == 0) { 1467 //add the schema minor version number column. 1468 statement.execute("ALTER TABLE tsk_db_info ADD COLUMN schema_minor_ver INTEGER DEFAULT 1"); 1469 } 1470 return new CaseDbSchemaVersionNumber(7, 1); 1471 1472 } finally { 1473 closeResultSet(resultSet); 1474 closeStatement(statement); 1475 releaseSingleUserCaseWriteLock(); 1476 } 1477 } 1478 1479 /** 1480 * Updates a schema version 7.1 database to a schema version 7.2 database. 1481 * 1482 * @param schemaVersion The current schema version of the database. 1483 * @param connection A connection to the case database. 1484 * 1485 * @return The new database schema version. 1486 * 1487 * @throws SQLException If there is an error completing a database 1488 * operation. 1489 * @throws TskCoreException If there is an error completing a database 1490 * operation via another SleuthkitCase method. 1491 */ updateFromSchema7dot1toSchema7dot2(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection)1492 private CaseDbSchemaVersionNumber updateFromSchema7dot1toSchema7dot2(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { 1493 if (schemaVersion.getMajor() != 7) { 1494 return schemaVersion; 1495 } 1496 1497 if (schemaVersion.getMinor() != 1) { 1498 return schemaVersion; 1499 } 1500 1501 Statement statement = null; 1502 Statement updstatement = null; 1503 ResultSet resultSet = null; 1504 acquireSingleUserCaseWriteLock(); 1505 try { 1506 //add the data_source_obj_id column to blackboard_artifacts. 1507 statement = connection.createStatement(); 1508 statement.execute("ALTER TABLE blackboard_artifacts ADD COLUMN data_source_obj_id INTEGER NOT NULL DEFAULT -1"); 1509 1510 // populate data_source_obj_id for each artifact 1511 updstatement = connection.createStatement(); 1512 resultSet = connection.executeQuery(statement, "SELECT artifact_id, obj_id FROM blackboard_artifacts"); //NON-NLS 1513 while (resultSet.next()) { 1514 long artifact_id = resultSet.getLong("artifact_id"); 1515 long obj_id = resultSet.getLong("obj_id"); 1516 long data_source_obj_id = getDataSourceObjectId(connection, obj_id); 1517 updstatement.executeUpdate("UPDATE blackboard_artifacts SET data_source_obj_id = " + data_source_obj_id + " " 1518 + "WHERE artifact_id = " + artifact_id); 1519 } 1520 closeResultSet(resultSet); 1521 closeStatement(statement); 1522 closeStatement(updstatement); 1523 1524 /* 1525 * Add a knownStatus column to the tag_names table. 1526 */ 1527 statement = connection.createStatement(); 1528 statement.execute("ALTER TABLE tag_names ADD COLUMN knownStatus INTEGER NOT NULL DEFAULT " + TskData.FileKnown.UNKNOWN.getFileKnownValue()); 1529 1530 // Create account_types, accounts, and account_relationships table 1531 if (this.dbType.equals(DbType.SQLITE)) { 1532 statement.execute("CREATE TABLE account_types (account_type_id INTEGER PRIMARY KEY, type_name TEXT UNIQUE NOT NULL, display_name TEXT NOT NULL)"); 1533 statement.execute("CREATE TABLE accounts (account_id INTEGER PRIMARY KEY, account_type_id INTEGER NOT NULL, account_unique_identifier TEXT NOT NULL, UNIQUE(account_type_id, account_unique_identifier) , FOREIGN KEY(account_type_id) REFERENCES account_types(account_type_id))"); 1534 statement.execute("CREATE TABLE account_relationships (relationship_id INTEGER PRIMARY KEY, account1_id INTEGER NOT NULL, account2_id INTEGER NOT NULL, relationship_source_obj_id INTEGER NOT NULL, date_time INTEGER, relationship_type INTEGER NOT NULL, data_source_obj_id INTEGER NOT NULL, UNIQUE(account1_id, account2_id, relationship_source_obj_id), FOREIGN KEY(account1_id) REFERENCES accounts(account_id), FOREIGN KEY(account2_id) REFERENCES accounts(account_id), FOREIGN KEY(relationship_source_obj_id) REFERENCES tsk_objects(obj_id), FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id))"); 1535 } else { 1536 statement.execute("CREATE TABLE account_types (account_type_id BIGSERIAL PRIMARY KEY, type_name TEXT UNIQUE NOT NULL, display_name TEXT NOT NULL)"); 1537 statement.execute("CREATE TABLE accounts (account_id BIGSERIAL PRIMARY KEY, account_type_id INTEGER NOT NULL, account_unique_identifier TEXT NOT NULL, UNIQUE(account_type_id, account_unique_identifier) , FOREIGN KEY(account_type_id) REFERENCES account_types(account_type_id))"); 1538 statement.execute("CREATE TABLE account_relationships (relationship_id BIGSERIAL PRIMARY KEY, account1_id INTEGER NOT NULL, account2_id INTEGER NOT NULL, relationship_source_obj_id INTEGER NOT NULL, date_time BIGINT, relationship_type INTEGER NOT NULL, data_source_obj_id INTEGER NOT NULL, UNIQUE(account1_id, account2_id, relationship_source_obj_id), FOREIGN KEY(account1_id) REFERENCES accounts(account_id), FOREIGN KEY(account2_id) REFERENCES accounts(account_id), FOREIGN KEY(relationship_source_obj_id) REFERENCES tsk_objects(obj_id), FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id))"); 1539 } 1540 1541 // Create indexes 1542 statement.execute("CREATE INDEX artifact_artifact_objID ON blackboard_artifacts(artifact_obj_id)"); 1543 statement.execute("CREATE INDEX relationships_account1 ON account_relationships(account1_id)"); 1544 statement.execute("CREATE INDEX relationships_account2 ON account_relationships(account2_id)"); 1545 statement.execute("CREATE INDEX relationships_relationship_source_obj_id ON account_relationships(relationship_source_obj_id)"); 1546 statement.execute("CREATE INDEX relationships_date_time ON account_relationships(date_time)"); 1547 statement.execute("CREATE INDEX relationships_relationship_type ON account_relationships(relationship_type)"); 1548 statement.execute("CREATE INDEX relationships_data_source_obj_id ON account_relationships(data_source_obj_id)"); 1549 1550 return new CaseDbSchemaVersionNumber(7, 2); 1551 } finally { 1552 closeResultSet(resultSet); 1553 closeStatement(statement); 1554 closeStatement(updstatement); 1555 releaseSingleUserCaseWriteLock(); 1556 } 1557 } 1558 1559 /** 1560 * Updates a schema version 7.2 database to a schema version 8.0 database. 1561 * 1562 * @param schemaVersion The current schema version of the database. 1563 * @param connection A connection to the case database. 1564 * 1565 * @return The new database schema version. 1566 * 1567 * @throws SQLException If there is an error completing a database 1568 * operation. 1569 * @throws TskCoreException If there is an error completing a database 1570 * operation via another SleuthkitCase method. 1571 */ updateFromSchema7dot2toSchema8dot0(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection)1572 private CaseDbSchemaVersionNumber updateFromSchema7dot2toSchema8dot0(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { 1573 if (schemaVersion.getMajor() != 7) { 1574 return schemaVersion; 1575 } 1576 1577 if (schemaVersion.getMinor() != 2) { 1578 return schemaVersion; 1579 } 1580 1581 Statement updateSchemaStatement = connection.createStatement(); 1582 Statement getExistingReportsStatement = connection.createStatement(); 1583 ResultSet resultSet = null; 1584 ResultSet existingReports = null; 1585 1586 acquireSingleUserCaseWriteLock(); 1587 try { 1588 // Update the schema to turn report_id into an object id. 1589 1590 // Unfortunately, SQLite doesn't support adding a constraint 1591 // to an existing table so we have to rename the old... 1592 updateSchemaStatement.execute("ALTER TABLE reports RENAME TO old_reports"); 1593 1594 // ...create the new... 1595 updateSchemaStatement.execute("CREATE TABLE reports (obj_id BIGSERIAL PRIMARY KEY, path TEXT NOT NULL, crtime INTEGER NOT NULL, src_module_name TEXT NOT NULL, report_name TEXT NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id))"); 1596 1597 // ...add the existing report records back... 1598 existingReports = getExistingReportsStatement.executeQuery("SELECT * FROM old_reports"); 1599 while (existingReports.next()) { 1600 String path = existingReports.getString(2); 1601 long crtime = existingReports.getInt(3); 1602 String sourceModule = existingReports.getString(4); 1603 String reportName = existingReports.getString(5); 1604 1605 PreparedStatement insertObjectStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_OBJECT, Statement.RETURN_GENERATED_KEYS); 1606 insertObjectStatement.clearParameters(); 1607 insertObjectStatement.setNull(1, java.sql.Types.BIGINT); 1608 insertObjectStatement.setLong(2, TskData.ObjectType.REPORT.getObjectType()); 1609 connection.executeUpdate(insertObjectStatement); 1610 resultSet = insertObjectStatement.getGeneratedKeys(); 1611 if (!resultSet.next()) { 1612 throw new TskCoreException(String.format("Failed to INSERT report %s (%s) in tsk_objects table", reportName, path)); 1613 } 1614 long objectId = resultSet.getLong(1); //last_insert_rowid() 1615 1616 // INSERT INTO reports (obj_id, path, crtime, src_module_name, display_name) VALUES (?, ?, ?, ?, ?) 1617 PreparedStatement insertReportStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_REPORT); 1618 insertReportStatement.clearParameters(); 1619 insertReportStatement.setLong(1, objectId); 1620 insertReportStatement.setString(2, path); 1621 insertReportStatement.setLong(3, crtime); 1622 insertReportStatement.setString(4, sourceModule); 1623 insertReportStatement.setString(5, reportName); 1624 connection.executeUpdate(insertReportStatement); 1625 } 1626 1627 // ...and drop the old table. 1628 updateSchemaStatement.execute("DROP TABLE old_reports"); 1629 1630 return new CaseDbSchemaVersionNumber(8, 0); 1631 } finally { 1632 closeResultSet(resultSet); 1633 closeResultSet(existingReports); 1634 closeStatement(updateSchemaStatement); 1635 closeStatement(getExistingReportsStatement); 1636 releaseSingleUserCaseWriteLock(); 1637 } 1638 } 1639 1640 /** 1641 * Updates a schema version 8.0 database to a schema version 8.1 database. 1642 * 1643 * @param schemaVersion The current schema version of the database. 1644 * @param connection A connection to the case database. 1645 * 1646 * @return The new database schema version. 1647 * 1648 * @throws SQLException If there is an error completing a database 1649 * operation. 1650 * @throws TskCoreException If there is an error completing a database 1651 * operation via another SleuthkitCase method. 1652 */ updateFromSchema8dot0toSchema8dot1(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection)1653 private CaseDbSchemaVersionNumber updateFromSchema8dot0toSchema8dot1(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { 1654 if (schemaVersion.getMajor() != 8) { 1655 return schemaVersion; 1656 } 1657 1658 if (schemaVersion.getMinor() != 0) { 1659 return schemaVersion; 1660 } 1661 1662 acquireSingleUserCaseWriteLock(); 1663 1664 try (Statement statement = connection.createStatement();) { 1665 // create examiners table 1666 if (this.dbType.equals(DbType.SQLITE)) { 1667 statement.execute("CREATE TABLE tsk_examiners (examiner_id INTEGER PRIMARY KEY, login_name TEXT NOT NULL, display_name TEXT, UNIQUE(login_name) )"); 1668 statement.execute("ALTER TABLE content_tags ADD COLUMN examiner_id INTEGER REFERENCES tsk_examiners(examiner_id) DEFAULT NULL"); 1669 statement.execute("ALTER TABLE blackboard_artifact_tags ADD COLUMN examiner_id INTEGER REFERENCES tsk_examiners(examiner_id) DEFAULT NULL"); 1670 } else { 1671 statement.execute("CREATE TABLE tsk_examiners (examiner_id BIGSERIAL PRIMARY KEY, login_name TEXT NOT NULL, display_name TEXT, UNIQUE(login_name))"); 1672 statement.execute("ALTER TABLE content_tags ADD COLUMN examiner_id BIGINT REFERENCES tsk_examiners(examiner_id) DEFAULT NULL"); 1673 statement.execute("ALTER TABLE blackboard_artifact_tags ADD COLUMN examiner_id BIGINT REFERENCES tsk_examiners(examiner_id) DEFAULT NULL"); 1674 } 1675 1676 return new CaseDbSchemaVersionNumber(8, 1); 1677 } finally { 1678 releaseSingleUserCaseWriteLock(); 1679 } 1680 } 1681 1682 /** 1683 * Updates a schema version 8.1 database to a schema version 8.2 database. 1684 * 1685 * @param schemaVersion The current schema version of the database. 1686 * @param connection A connection to the case database. 1687 * 1688 * @return The new database schema version. 1689 * 1690 * @throws SQLException If there is an error completing a database 1691 * operation. 1692 * @throws TskCoreException If there is an error completing a database 1693 * operation via another SleuthkitCase method. 1694 */ updateFromSchema8dot1toSchema8dot2(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection)1695 private CaseDbSchemaVersionNumber updateFromSchema8dot1toSchema8dot2(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { 1696 if (schemaVersion.getMajor() != 8) { 1697 return schemaVersion; 1698 } 1699 1700 if (schemaVersion.getMinor() != 1) { 1701 return schemaVersion; 1702 } 1703 1704 acquireSingleUserCaseWriteLock(); 1705 1706 try (Statement statement = connection.createStatement();) { 1707 statement.execute("ALTER TABLE tsk_image_info ADD COLUMN sha1 TEXT DEFAULT NULL"); 1708 statement.execute("ALTER TABLE tsk_image_info ADD COLUMN sha256 TEXT DEFAULT NULL"); 1709 1710 statement.execute("ALTER TABLE data_source_info ADD COLUMN acquisition_details TEXT"); 1711 1712 /* 1713 * Add new tsk_db_extended_info table with TSK version, creation 1714 * time schema and schema version numbers as the initial data. The 1715 * creation time schema version is set to 0, 0 to indicate that it 1716 * is not known. 1717 */ 1718 statement.execute("CREATE TABLE tsk_db_info_extended (name TEXT PRIMARY KEY, value TEXT NOT NULL)"); 1719 ResultSet result = statement.executeQuery("SELECT tsk_ver FROM tsk_db_info"); 1720 result.next(); 1721 statement.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('" + TSK_VERSION_KEY + "', '" + result.getLong("tsk_ver") + "')"); 1722 statement.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('" + SCHEMA_MAJOR_VERSION_KEY + "', '8')"); 1723 statement.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('" + SCHEMA_MINOR_VERSION_KEY + "', '2')"); 1724 statement.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('" + CREATION_SCHEMA_MAJOR_VERSION_KEY + "', '0')"); 1725 statement.execute("INSERT INTO tsk_db_info_extended (name, value) VALUES ('" + CREATION_SCHEMA_MINOR_VERSION_KEY + "', '0')"); 1726 1727 String primaryKeyType; 1728 switch (getDatabaseType()) { 1729 case POSTGRESQL: 1730 primaryKeyType = "BIGSERIAL"; 1731 break; 1732 case SQLITE: 1733 primaryKeyType = "INTEGER"; 1734 break; 1735 default: 1736 throw new TskCoreException("Unsupported data base type: " + getDatabaseType().toString()); 1737 } 1738 1739 //create and initialize tsk_event_types tables 1740 statement.execute("CREATE TABLE tsk_event_types (" 1741 + " event_type_id " + primaryKeyType + " PRIMARY KEY, " 1742 + " display_name TEXT UNIQUE NOT NULL, " 1743 + " super_type_id INTEGER REFERENCES tsk_event_types(event_type_id) )"); 1744 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1745 + " values( 0, 'Event Types', null)"); 1746 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1747 + " values(1, 'File System', 0)"); 1748 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1749 + " values(2, 'Web Activity', 0)"); 1750 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1751 + " values(3, 'Misc Types', 0)"); 1752 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1753 + " values(4, 'Modified', 1)"); 1754 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1755 + " values(5, 'Accessed', 1)"); 1756 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1757 + " values(6, 'Created', 1)"); 1758 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1759 + " values(7, 'Changed', 1)"); 1760 1761 //create tsk_events tables 1762 statement.execute("CREATE TABLE tsk_event_descriptions (" 1763 + " event_description_id " + primaryKeyType + " PRIMARY KEY, " 1764 + " full_description TEXT NOT NULL, " 1765 + " med_description TEXT, " 1766 + " short_description TEXT," 1767 + " data_source_obj_id BIGINT NOT NULL, " 1768 + " file_obj_id BIGINT NOT NULL, " 1769 + " artifact_id BIGINT, " 1770 + " hash_hit INTEGER NOT NULL, " //boolean 1771 + " tagged INTEGER NOT NULL, " //boolean 1772 + " FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id), " 1773 + " FOREIGN KEY(file_obj_id) REFERENCES tsk_files(obj_id), " 1774 + " FOREIGN KEY(artifact_id) REFERENCES blackboard_artifacts(artifact_id))" 1775 ); 1776 1777 statement.execute("CREATE TABLE tsk_events ( " 1778 + " event_id " + primaryKeyType + " PRIMARY KEY, " 1779 + " event_type_id BIGINT NOT NULL REFERENCES tsk_event_types(event_type_id) ," 1780 + " event_description_id BIGINT NOT NULL REFERENCES tsk_event_descriptions(event_description_id) ," 1781 + " time INTEGER NOT NULL) " 1782 ); 1783 1784 //create tsk_events indices 1785 statement.execute("CREATE INDEX events_time ON tsk_events(time)"); 1786 statement.execute("CREATE INDEX events_type ON tsk_events(event_type_id)"); 1787 statement.execute("CREATE INDEX events_data_source_obj_id ON tsk_event_descriptions(data_source_obj_id) "); 1788 statement.execute("CREATE INDEX events_file_obj_id ON tsk_event_descriptions(file_obj_id "); 1789 statement.execute("CREATE INDEX events_artifact_id ON tsk_event_descriptions(artifact_id) "); 1790 statement.execute("CREATE INDEX events_sub_type_time ON tsk_events(event_type_id, time) "); 1791 statement.execute("CREATE INDEX events_time ON tsk_events(time "); 1792 return new CaseDbSchemaVersionNumber(8, 2); 1793 1794 } finally { 1795 releaseSingleUserCaseWriteLock(); 1796 } 1797 } 1798 1799 /** 1800 * Updates a schema version 8.2 database to a schema version 8.3 database. 1801 * 1802 * @param schemaVersion The current schema version of the database. 1803 * @param connection A connection to the case database. 1804 * 1805 * @return The new database schema version. 1806 * 1807 * @throws SQLException If there is an error completing a database 1808 * operation. 1809 * @throws TskCoreException If there is an error completing a database 1810 * operation via another SleuthkitCase method. 1811 */ updateFromSchema8dot2toSchema8dot3(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection)1812 private CaseDbSchemaVersionNumber updateFromSchema8dot2toSchema8dot3(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { 1813 if (schemaVersion.getMajor() != 8) { 1814 return schemaVersion; 1815 } 1816 1817 if (schemaVersion.getMinor() != 2) { 1818 return schemaVersion; 1819 } 1820 1821 acquireSingleUserCaseWriteLock(); 1822 1823 ResultSet resultSet = null; 1824 1825 try (Statement statement = connection.createStatement();) { 1826 1827 // Add the uniqueness constraint to the tsk_event and tsk_event_description tables. 1828 // Unfortunately, SQLite doesn't support adding a constraint 1829 // to an existing table so we have to rename the old... 1830 String primaryKeyType; 1831 switch (getDatabaseType()) { 1832 case POSTGRESQL: 1833 primaryKeyType = "BIGSERIAL"; 1834 break; 1835 case SQLITE: 1836 primaryKeyType = "INTEGER"; 1837 break; 1838 default: 1839 throw new TskCoreException("Unsupported data base type: " + getDatabaseType().toString()); 1840 } 1841 1842 //create and initialize tsk_event_types tables which may or may not exist 1843 statement.execute("CREATE TABLE IF NOT EXISTS tsk_event_types (" 1844 + " event_type_id " + primaryKeyType + " PRIMARY KEY, " 1845 + " display_name TEXT UNIQUE NOT NULL, " 1846 + " super_type_id INTEGER REFERENCES tsk_event_types(event_type_id) )"); 1847 1848 resultSet = statement.executeQuery("SELECT * from tsk_event_types"); 1849 1850 // If there is something in resultSet then the table must have previously 1851 // existing therefore there is not need to populate 1852 if (!resultSet.next()) { 1853 1854 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1855 + " values( 0, 'Event Types', null)"); 1856 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1857 + " values(1, 'File System', 0)"); 1858 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1859 + " values(2, 'Web Activity', 0)"); 1860 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1861 + " values(3, 'Misc Types', 0)"); 1862 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1863 + " values(4, 'Modified', 1)"); 1864 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1865 + " values(5, 'Accessed', 1)"); 1866 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1867 + " values(6, 'Created', 1)"); 1868 statement.execute("insert into tsk_event_types(event_type_id, display_name, super_type_id)" 1869 + " values(7, 'Changed', 1)"); 1870 } 1871 1872 // Delete the old table that may have been created with the upgrade 1873 // from 8.1 to 8.2. 1874 statement.execute("DROP TABLE IF EXISTS tsk_events"); 1875 1876 // Delete the old table that may have been created with the upgrade 1877 // from 8.1 to 8.2 1878 statement.execute("DROP TABLE IF EXISTS tsk_event_descriptions"); 1879 1880 //create new tsk_event_description table 1881 statement.execute("CREATE TABLE tsk_event_descriptions (" 1882 + " event_description_id " + primaryKeyType + " PRIMARY KEY, " 1883 + " full_description TEXT NOT NULL, " 1884 + " med_description TEXT, " 1885 + " short_description TEXT," 1886 + " data_source_obj_id BIGINT NOT NULL, " 1887 + " file_obj_id BIGINT NOT NULL, " 1888 + " artifact_id BIGINT, " 1889 + " hash_hit INTEGER NOT NULL, " //boolean 1890 + " tagged INTEGER NOT NULL, " //boolean 1891 + " UNIQUE(full_description, file_obj_id, artifact_id), " 1892 + " FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id), " 1893 + " FOREIGN KEY(file_obj_id) REFERENCES tsk_files(obj_id), " 1894 + " FOREIGN KEY(artifact_id) REFERENCES blackboard_artifacts(artifact_id))" 1895 ); 1896 1897 // create a new table 1898 statement.execute("CREATE TABLE tsk_events ( " 1899 + " event_id " + primaryKeyType + " PRIMARY KEY, " 1900 + " event_type_id BIGINT NOT NULL REFERENCES tsk_event_types(event_type_id) ," 1901 + " event_description_id BIGINT NOT NULL REFERENCES tsk_event_descriptions(event_description_id) ," 1902 + " time INTEGER NOT NULL, " 1903 + " UNIQUE (event_type_id, event_description_id, time))" 1904 ); 1905 1906 // Fix mistakenly set names in tsk_db_info_extended 1907 statement.execute("UPDATE tsk_db_info_extended SET name = 'CREATION_SCHEMA_MAJOR_VERION' WHERE name = 'CREATED_SCHEMA_MAJOR_VERSION'"); 1908 statement.execute("UPDATE tsk_db_info_extended SET name = 'CREATION_SCHEMA_MINOR_VERION' WHERE name = 'CREATED_SCHEMA_MINOR_VERSION'"); 1909 1910 return new CaseDbSchemaVersionNumber(8, 3); 1911 } finally { 1912 closeResultSet(resultSet); 1913 releaseSingleUserCaseWriteLock(); 1914 } 1915 } 1916 1917 /** 1918 * Extract the extension from a file name. 1919 * 1920 * @param fileName the file name to extract the extension from. 1921 * 1922 * @return The extension extracted from fileName. Will not be null. 1923 */ extractExtension(final String fileName)1924 static String extractExtension(final String fileName) { 1925 String ext; 1926 int i = fileName.lastIndexOf("."); 1927 // > 0 because we assume it's not an extension if period is the first character 1928 if ((i > 0) && ((i + 1) < fileName.length())) { 1929 ext = fileName.substring(i + 1); 1930 } else { 1931 return ""; 1932 } 1933 // we added this at one point to deal with files that had crazy names based on URLs 1934 // it's too hard though to clean those up and not mess up basic extensions though. 1935 // We need to add '-' to the below if we use it again 1936 // String[] findNonAlphanumeric = ext.split("[^a-zA-Z0-9_]"); 1937 // if (findNonAlphanumeric.length > 1) { 1938 // ext = findNonAlphanumeric[0]; 1939 // } 1940 return ext.toLowerCase(); 1941 } 1942 1943 /** 1944 * Returns case database schema version number. As of TSK 4.5.0 db schema 1945 * versions are two part Major.minor. This method only returns the major 1946 * part. Use getDBSchemaVersion() for the complete version. 1947 * 1948 * @return The schema version number as an integer. 1949 * 1950 * @deprecated since 4.5.0 Use getDBSchemaVersion() instead for more 1951 * complete version info. 1952 */ 1953 @Deprecated getSchemaVersion()1954 public int getSchemaVersion() { 1955 return getDBSchemaVersion().getMajor(); 1956 } 1957 1958 /** 1959 * Gets the database schema version in use. 1960 * 1961 * @return the database schema version in use. 1962 */ getDBSchemaVersion()1963 public VersionNumber getDBSchemaVersion() { 1964 return CURRENT_DB_SCHEMA_VERSION; 1965 } 1966 1967 /** 1968 * Gets the creation version of the database schema. 1969 * 1970 * @return the creation version for the database schema, the creation 1971 * version will be 0.0 for databases created prior to 8.2 1972 */ getDBSchemaCreationVersion()1973 public CaseDbSchemaVersionNumber getDBSchemaCreationVersion() { 1974 return caseDBSchemaCreationVersion; 1975 } 1976 1977 /** 1978 * Returns the type of database in use. 1979 * 1980 * @return database type 1981 */ getDatabaseType()1982 public DbType getDatabaseType() { 1983 return this.dbType; 1984 } 1985 1986 /** 1987 * Returns the path of a backup copy of the database made when a schema 1988 * version upgrade has occurred. 1989 * 1990 * @return The path of the backup file or null if no backup was made. 1991 */ getBackupDatabasePath()1992 public String getBackupDatabasePath() { 1993 return dbBackupPath; 1994 } 1995 1996 /** 1997 * Create a new transaction on the case database. The transaction object 1998 * that is returned can be passed to methods that take a CaseDbTransaction. 1999 * The caller is responsible for calling either commit() or rollback() on 2000 * the transaction object. 2001 * 2002 * @return A CaseDbTransaction object. 2003 * 2004 * @throws TskCoreException 2005 */ beginTransaction()2006 public CaseDbTransaction beginTransaction() throws TskCoreException { 2007 return new CaseDbTransaction(this, connections.getConnection()); 2008 } 2009 2010 /** 2011 * Gets the case database name. 2012 * 2013 * @return The case database name. 2014 */ getDatabaseName()2015 public String getDatabaseName() { 2016 return databaseName; 2017 } 2018 2019 /** 2020 * Get the full path to the case directory. For a SQLite case database, this 2021 * is the same as the database directory path. 2022 * 2023 * @return Case directory path. 2024 */ getDbDirPath()2025 public String getDbDirPath() { 2026 return caseDirPath; 2027 } 2028 2029 /** 2030 * Acquires a write lock, but only if this is a single-user case. Always 2031 * call this method in a try block with a call to the lock release method in 2032 * an associated finally block. 2033 */ acquireSingleUserCaseWriteLock()2034 public void acquireSingleUserCaseWriteLock() { 2035 if (dbType == DbType.SQLITE) { 2036 rwLock.writeLock().lock(); 2037 } 2038 } 2039 2040 /** 2041 * Releases a write lock, but only if this is a single-user case. This 2042 * method should always be called in the finally block of a try block in 2043 * which the lock was acquired. 2044 */ releaseSingleUserCaseWriteLock()2045 public void releaseSingleUserCaseWriteLock() { 2046 if (dbType == DbType.SQLITE) { 2047 rwLock.writeLock().unlock(); 2048 } 2049 } 2050 2051 /** 2052 * Acquires a read lock, but only if this is a single-user case. Call this 2053 * method in a try block with a call to the lock release method in an 2054 * associated finally block. 2055 */ acquireSingleUserCaseReadLock()2056 public void acquireSingleUserCaseReadLock() { 2057 if (dbType == DbType.SQLITE) { 2058 rwLock.readLock().lock(); 2059 } 2060 } 2061 2062 /** 2063 * Releases a read lock, but only if this is a single-user case. This method 2064 * should always be called in the finally block of a try block in which the 2065 * lock was acquired. 2066 */ releaseSingleUserCaseReadLock()2067 public void releaseSingleUserCaseReadLock() { 2068 if (dbType == DbType.SQLITE) { 2069 rwLock.readLock().unlock(); 2070 } 2071 } 2072 2073 /** 2074 * Open an existing case database. 2075 * 2076 * @param dbPath Path to SQLite case database. 2077 * 2078 * @return Case database object. 2079 * 2080 * @throws org.sleuthkit.datamodel.TskCoreException 2081 */ openCase(String dbPath)2082 public static SleuthkitCase openCase(String dbPath) throws TskCoreException { 2083 try { 2084 final SleuthkitJNI.CaseDbHandle caseHandle = SleuthkitJNI.openCaseDb(dbPath); 2085 return new SleuthkitCase(dbPath, caseHandle, DbType.SQLITE); 2086 } catch (TskUnsupportedSchemaVersionException ex) { 2087 //don't wrap in new TskCoreException 2088 throw ex; 2089 } catch (Exception ex) { 2090 throw new TskCoreException("Failed to open case database at " + dbPath, ex); 2091 } 2092 } 2093 2094 /** 2095 * Open an existing multi-user case database. 2096 * 2097 * @param databaseName The name of the database. 2098 * @param info Connection information for the the database. 2099 * @param caseDir The folder where the case metadata fils is stored. 2100 * 2101 * @return A case database object. 2102 * 2103 * @throws TskCoreException If there is a problem opening the database. 2104 */ openCase(String databaseName, CaseDbConnectionInfo info, String caseDir)2105 public static SleuthkitCase openCase(String databaseName, CaseDbConnectionInfo info, String caseDir) throws TskCoreException { 2106 try { 2107 /* 2108 * The flow of this method involves trying to open case and if 2109 * successful, return that case. If unsuccessful, an exception is 2110 * thrown. We catch any exceptions, and use tryConnect() to attempt 2111 * to obtain further information about the error. If tryConnect() is 2112 * unable to successfully connect, tryConnect() will throw a 2113 * TskCoreException with a message containing user-level error 2114 * reporting. If tryConnect() is able to connect, flow continues and 2115 * we rethrow the original exception obtained from trying to create 2116 * the case. In this way, we obtain more detailed information if we 2117 * are able, but do not lose any information if unable. 2118 */ 2119 final SleuthkitJNI.CaseDbHandle caseHandle = SleuthkitJNI.openCaseDb(databaseName, info); 2120 return new SleuthkitCase(info.getHost(), Integer.parseInt(info.getPort()), databaseName, info.getUserName(), info.getPassword(), caseHandle, caseDir, info.getDbType()); 2121 } catch (PropertyVetoException exp) { 2122 // In this case, the JDBC driver doesn't support PostgreSQL. Use the generic message here. 2123 throw new TskCoreException(exp.getMessage(), exp); 2124 } catch (TskUnsupportedSchemaVersionException ex) { 2125 //don't wrap in new TskCoreException 2126 throw ex; 2127 } catch (Exception exp) { 2128 tryConnect(info); // attempt to connect, throw with user-friendly message if unable 2129 throw new TskCoreException(exp.getMessage(), exp); // throw with generic message if tryConnect() was successful 2130 } 2131 } 2132 2133 /** 2134 * Creates a new SQLite case database. 2135 * 2136 * @param dbPath Path to where SQlite case database should be created. 2137 * 2138 * @return A case database object. 2139 * 2140 * @throws org.sleuthkit.datamodel.TskCoreException 2141 */ newCase(String dbPath)2142 public static SleuthkitCase newCase(String dbPath) throws TskCoreException { 2143 try { 2144 SleuthkitJNI.CaseDbHandle caseHandle = SleuthkitJNI.newCaseDb(dbPath); 2145 return new SleuthkitCase(dbPath, caseHandle, DbType.SQLITE); 2146 } catch (Exception ex) { 2147 throw new TskCoreException("Failed to create case database at " + dbPath, ex); 2148 } 2149 } 2150 2151 /** 2152 * Creates a new PostgreSQL case database. 2153 * 2154 * @param caseName The name of the case. It will be used to create a case 2155 * database name that can be safely used in SQL commands 2156 * and will not be subject to name collisions on the case 2157 * database server. Use getDatabaseName to get the 2158 * created name. 2159 * @param info The information to connect to the database. 2160 * @param caseDirPath The case directory path. 2161 * 2162 * @return A case database object. 2163 * 2164 * @throws org.sleuthkit.datamodel.TskCoreException 2165 */ newCase(String caseName, CaseDbConnectionInfo info, String caseDirPath)2166 public static SleuthkitCase newCase(String caseName, CaseDbConnectionInfo info, String caseDirPath) throws TskCoreException { 2167 String databaseName = createCaseDataBaseName(caseName); 2168 try { 2169 /** 2170 * The flow of this method involves trying to create a new case and 2171 * if successful, return that case. If unsuccessful, an exception is 2172 * thrown. We catch any exceptions, and use tryConnect() to attempt 2173 * to obtain further information about the error. If tryConnect() is 2174 * unable to successfully connect, tryConnect() will throw a 2175 * TskCoreException with a message containing user-level error 2176 * reporting. If tryConnect() is able to connect, flow continues and 2177 * we rethrow the original exception obtained from trying to create 2178 * the case. In this way, we obtain more detailed information if we 2179 * are able, but do not lose any information if unable. 2180 */ 2181 SleuthkitJNI.CaseDbHandle caseHandle = SleuthkitJNI.newCaseDb(databaseName, info); 2182 return new SleuthkitCase(info.getHost(), Integer.parseInt(info.getPort()), 2183 databaseName, info.getUserName(), info.getPassword(), caseHandle, caseDirPath, info.getDbType()); 2184 } catch (PropertyVetoException exp) { 2185 // In this case, the JDBC driver doesn't support PostgreSQL. Use the generic message here. 2186 throw new TskCoreException(exp.getMessage(), exp); 2187 } catch (Exception exp) { 2188 tryConnect(info); // attempt to connect, throw with user-friendly message if unable 2189 throw new TskCoreException(exp.getMessage(), exp); // throw with generic message if tryConnect() was successful 2190 } 2191 } 2192 2193 /** 2194 * Transforms a candidate PostgreSQL case database name into one that can be 2195 * safely used in SQL commands and will not be subject to name collisions on 2196 * the case database server. 2197 * 2198 * @param candidateDbName A candidate case database name. 2199 * 2200 * @return A case database name. 2201 */ createCaseDataBaseName(String candidateDbName)2202 private static String createCaseDataBaseName(String candidateDbName) { 2203 String dbName; 2204 if (!candidateDbName.isEmpty()) { 2205 /* 2206 * Replace all non-ASCII characters. 2207 */ 2208 dbName = candidateDbName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS 2209 2210 /* 2211 * Replace all control characters. 2212 */ 2213 dbName = dbName.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS 2214 2215 /* 2216 * Replace /, \, :, ?, space, ' ". 2217 */ 2218 dbName = dbName.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS 2219 2220 /* 2221 * Make it all lowercase. 2222 */ 2223 dbName = dbName.toLowerCase(); 2224 2225 /* 2226 * Must start with letter or underscore. If not, prepend an 2227 * underscore. 2228 */ 2229 if ((dbName.length() > 0 && !(Character.isLetter(dbName.codePointAt(0))) && !(dbName.codePointAt(0) == '_'))) { 2230 dbName = "_" + dbName; 2231 } 2232 2233 /* 2234 * Truncate to 63 - 16 = 47 chars to accomodate a timestamp for 2235 * uniqueness. 2236 */ 2237 if (dbName.length() > MAX_DB_NAME_LEN_BEFORE_TIMESTAMP) { 2238 dbName = dbName.substring(0, MAX_DB_NAME_LEN_BEFORE_TIMESTAMP); 2239 } 2240 2241 } else { 2242 /* 2243 * Must start with letter or underscore. 2244 */ 2245 dbName = "_"; 2246 } 2247 /* 2248 * Add the time stmap. 2249 */ 2250 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss"); 2251 Date date = new Date(); 2252 dbName = dbName + "_" + dateFormat.format(date); 2253 2254 return dbName; 2255 } 2256 2257 /** 2258 * Returns the Examiner object for currently logged in user 2259 * 2260 * @return A Examiner object. 2261 * 2262 * @throws org.sleuthkit.datamodel.TskCoreException 2263 */ getCurrentExaminer()2264 public Examiner getCurrentExaminer() throws TskCoreException { 2265 2266 // return cached value if there's one 2267 if (cachedCurrentExaminer != null) { 2268 return cachedCurrentExaminer; 2269 } 2270 String loginName = System.getProperty("user.name"); 2271 if (loginName == null || loginName.isEmpty()) { 2272 throw new TskCoreException("Failed to determine logged in user name."); 2273 } 2274 2275 CaseDbConnection connection = connections.getConnection(); 2276 acquireSingleUserCaseReadLock(); 2277 ResultSet resultSet = null; 2278 try { 2279 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_EXAMINER_BY_LOGIN_NAME); 2280 statement.clearParameters(); 2281 statement.setString(1, loginName); 2282 resultSet = connection.executeQuery(statement); 2283 if (resultSet.next()) { 2284 cachedCurrentExaminer = new Examiner(resultSet.getLong("examiner_id"), resultSet.getString("login_name"), resultSet.getString("display_name")); 2285 return cachedCurrentExaminer; 2286 } else { 2287 throw new TskCoreException("Error getting examaminer for name = " + loginName); 2288 } 2289 2290 } catch (SQLException ex) { 2291 throw new TskCoreException("Error getting examaminer for name = " + loginName, ex); 2292 } finally { 2293 closeResultSet(resultSet); 2294 connection.close(); 2295 releaseSingleUserCaseReadLock(); 2296 } 2297 2298 } 2299 2300 /** 2301 * Returns the Examiner object for given id 2302 * 2303 * @param id 2304 * 2305 * @return Examiner object 2306 * 2307 * @throws TskCoreException 2308 */ getExaminerById(long id)2309 Examiner getExaminerById(long id) throws TskCoreException { 2310 2311 CaseDbConnection connection = connections.getConnection(); 2312 acquireSingleUserCaseReadLock(); 2313 ResultSet resultSet = null; 2314 try { 2315 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_EXAMINER_BY_ID); 2316 statement.clearParameters(); 2317 statement.setLong(1, id); 2318 resultSet = connection.executeQuery(statement); 2319 if (resultSet.next()) { 2320 return new Examiner(resultSet.getLong("examiner_id"), resultSet.getString("login_name"), resultSet.getString("full_name")); 2321 } else { 2322 throw new TskCoreException("Error getting examaminer for id = " + id); 2323 } 2324 } catch (SQLException ex) { 2325 throw new TskCoreException("Error getting examaminer for id = " + id, ex); 2326 } finally { 2327 closeResultSet(resultSet); 2328 connection.close(); 2329 releaseSingleUserCaseReadLock(); 2330 } 2331 } 2332 2333 /** 2334 * Starts the multi-step process of adding an image data source to the case 2335 * by creating an object that can be used to control the process and get 2336 * progress messages from it. 2337 * 2338 * @param timeZone The time zone of the image. 2339 * @param addUnallocSpace Set to true to create virtual files for 2340 * unallocated space in the image. 2341 * @param noFatFsOrphans Set to true to skip processing orphan files of FAT 2342 * file systems. 2343 * @param imageCopyPath Path to which a copy of the image should be 2344 * written. Use the empty string to disable image 2345 * writing. 2346 * 2347 * @return An object that encapsulates control of adding an image via the 2348 * SleuthKit native code layer. 2349 */ makeAddImageProcess(String timeZone, boolean addUnallocSpace, boolean noFatFsOrphans, String imageCopyPath)2350 public AddImageProcess makeAddImageProcess(String timeZone, boolean addUnallocSpace, boolean noFatFsOrphans, String imageCopyPath) { 2351 return this.caseHandle.initAddImageProcess(timeZone, addUnallocSpace, noFatFsOrphans, imageCopyPath, this); 2352 } 2353 2354 /** 2355 * Get the list of root objects (data sources) from the case database, e.g., 2356 * image files, logical (local) files, virtual directories. 2357 * 2358 * @return List of content objects representing root objects. 2359 * 2360 * @throws TskCoreException 2361 */ getRootObjects()2362 public List<Content> getRootObjects() throws TskCoreException { 2363 CaseDbConnection connection = connections.getConnection(); 2364 acquireSingleUserCaseReadLock(); 2365 Statement s = null; 2366 ResultSet rs = null; 2367 try { 2368 s = connection.createStatement(); 2369 rs = connection.executeQuery(s, "SELECT obj_id, type FROM tsk_objects " //NON-NLS 2370 + "WHERE par_obj_id IS NULL"); //NON-NLS 2371 Collection<ObjectInfo> infos = new ArrayList<ObjectInfo>(); 2372 while (rs.next()) { 2373 infos.add(new ObjectInfo(rs.getLong("obj_id"), ObjectType.valueOf(rs.getShort("type")))); //NON-NLS 2374 } 2375 2376 List<Content> rootObjs = new ArrayList<Content>(); 2377 for (ObjectInfo i : infos) { 2378 if (null != i.type) { 2379 switch (i.type) { 2380 case IMG: 2381 rootObjs.add(getImageById(i.id)); 2382 break; 2383 case ABSTRACTFILE: 2384 // Check if virtual dir for local files. 2385 AbstractFile af = getAbstractFileById(i.id); 2386 if (af instanceof VirtualDirectory) { 2387 rootObjs.add(af); 2388 } else { 2389 throw new TskCoreException("Parentless object has wrong type to be a root (ABSTRACTFILE, but not VIRTUAL_DIRECTORY: " + i.type); 2390 } 2391 break; 2392 case REPORT: 2393 break; 2394 default: 2395 throw new TskCoreException("Parentless object has wrong type to be a root: " + i.type); 2396 } 2397 } 2398 } 2399 return rootObjs; 2400 } catch (SQLException ex) { 2401 throw new TskCoreException("Error getting root objects", ex); 2402 } finally { 2403 closeResultSet(rs); 2404 closeStatement(s); 2405 connection.close(); 2406 releaseSingleUserCaseReadLock(); 2407 } 2408 } 2409 2410 /** 2411 * Gets the the datasource obj ids for the given device_id 2412 * 2413 * @param deviceId device_id 2414 * 2415 * @return A list of the data source object_id for the given device_id for 2416 * the case. 2417 * 2418 * @throws TskCoreException if there is a problem getting the data source 2419 * obj ids. 2420 */ getDataSourceObjIds(String deviceId)2421 List<Long> getDataSourceObjIds(String deviceId) throws TskCoreException { 2422 2423 // check cached map first 2424 synchronized (deviceIdToDatasourceObjIdMap) { 2425 if (deviceIdToDatasourceObjIdMap.containsKey(deviceId)) { 2426 return new ArrayList<Long>(deviceIdToDatasourceObjIdMap.get(deviceId)); 2427 } 2428 2429 CaseDbConnection connection = connections.getConnection(); 2430 acquireSingleUserCaseReadLock(); 2431 Statement s = null; 2432 ResultSet rs = null; 2433 try { 2434 s = connection.createStatement(); 2435 rs = connection.executeQuery(s, "SELECT obj_id FROM data_source_info WHERE device_id = '" + deviceId + "'"); //NON-NLS 2436 List<Long> dataSourceObjIds = new ArrayList<Long>(); 2437 while (rs.next()) { 2438 dataSourceObjIds.add(rs.getLong("obj_id")); 2439 2440 // Add to map of deviceID to data_source_obj_id. 2441 long ds_obj_id = rs.getLong("obj_id"); 2442 if (deviceIdToDatasourceObjIdMap.containsKey(deviceId)) { 2443 deviceIdToDatasourceObjIdMap.get(deviceId).add(ds_obj_id); 2444 } else { 2445 deviceIdToDatasourceObjIdMap.put(deviceId, new HashSet<Long>(Arrays.asList(ds_obj_id))); 2446 } 2447 } 2448 return dataSourceObjIds; 2449 } catch (SQLException ex) { 2450 throw new TskCoreException("Error getting data sources", ex); 2451 } finally { 2452 closeResultSet(rs); 2453 closeStatement(s); 2454 connection.close(); 2455 releaseSingleUserCaseReadLock(); 2456 } 2457 } 2458 } 2459 2460 /** 2461 * Gets the data sources for the case. For each data source, if it is an 2462 * image, an Image will be instantiated. Otherwise, a LocalFilesDataSource 2463 * will be instantiated. 2464 * 2465 * NOTE: The DataSource interface is an emerging feature and at present is 2466 * only useful for obtaining the object id and the device id, an 2467 * ASCII-printable identifier for the device associated with the data source 2468 * that is intended to be unique across multiple cases (e.g., a UUID). In 2469 * the future, this method will be a replacement for the getRootObjects 2470 * method. 2471 * 2472 * @return A list of the data sources for the case. 2473 * 2474 * @throws TskCoreException if there is a problem getting the data sources. 2475 */ getDataSources()2476 public List<DataSource> getDataSources() throws TskCoreException { 2477 CaseDbConnection connection = connections.getConnection(); 2478 acquireSingleUserCaseReadLock(); 2479 Statement statement = null; 2480 ResultSet resultSet = null; 2481 Statement statement2 = null; 2482 ResultSet resultSet2 = null; 2483 try { 2484 statement = connection.createStatement(); 2485 statement2 = connection.createStatement(); 2486 resultSet = connection.executeQuery(statement, 2487 "SELECT ds.obj_id, ds.device_id, ds.time_zone, img.type, img.ssize, img.size, img.md5, img.sha1, img.sha256, img.display_name " 2488 + "FROM data_source_info AS ds " 2489 + "LEFT JOIN tsk_image_info AS img " 2490 + "ON ds.obj_id = img.obj_id"); //NON-NLS 2491 2492 List<DataSource> dataSourceList = new ArrayList<DataSource>(); 2493 Map<Long, List<String>> imagePathsMap = getImagePaths(); 2494 2495 while (resultSet.next()) { 2496 DataSource dataSource; 2497 Long objectId = resultSet.getLong("obj_id"); 2498 String deviceId = resultSet.getString("device_id"); 2499 String timezone = resultSet.getString("time_zone"); 2500 String type = resultSet.getString("type"); 2501 2502 if (type == null) { 2503 /* 2504 * No data found in 'tsk_image_info', so we build a 2505 * LocalFilesDataSource. 2506 */ 2507 2508 resultSet2 = connection.executeQuery(statement2, "SELECT name FROM tsk_files WHERE tsk_files.obj_id = " + objectId); //NON-NLS 2509 String dsName = (resultSet2.next()) ? resultSet2.getString("name") : ""; 2510 resultSet2.close(); 2511 2512 TSK_FS_NAME_TYPE_ENUM dirType = TSK_FS_NAME_TYPE_ENUM.DIR; 2513 TSK_FS_META_TYPE_ENUM metaType = TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR; 2514 TSK_FS_NAME_FLAG_ENUM dirFlag = TSK_FS_NAME_FLAG_ENUM.ALLOC; 2515 final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue() 2516 | TSK_FS_META_FLAG_ENUM.USED.getValue()); 2517 String parentPath = "/"; //NON-NLS 2518 dataSource = new LocalFilesDataSource(this, objectId, objectId, deviceId, dsName, dirType, metaType, dirFlag, metaFlags, timezone, null, FileKnown.UNKNOWN, parentPath); 2519 } else { 2520 /* 2521 * Data found in 'tsk_image_info', so we build an Image. 2522 */ 2523 Long ssize = resultSet.getLong("ssize"); 2524 Long size = resultSet.getLong("size"); 2525 String md5 = resultSet.getString("md5"); 2526 String sha1 = resultSet.getString("sha1"); 2527 String sha256 = resultSet.getString("sha256"); 2528 String name = resultSet.getString("display_name"); 2529 2530 List<String> imagePaths = imagePathsMap.get(objectId); 2531 if (name == null) { 2532 if (imagePaths.size() > 0) { 2533 String path = imagePaths.get(0); 2534 name = (new java.io.File(path)).getName(); 2535 } else { 2536 name = ""; 2537 } 2538 } 2539 2540 dataSource = new Image(this, objectId, Long.valueOf(type), deviceId, ssize, name, 2541 imagePaths.toArray(new String[imagePaths.size()]), timezone, md5, sha1, sha256, size); 2542 } 2543 2544 dataSourceList.add(dataSource); 2545 } 2546 2547 return dataSourceList; 2548 2549 } catch (SQLException ex) { 2550 throw new TskCoreException("Error getting data sources", ex); 2551 } finally { 2552 closeResultSet(resultSet); 2553 closeStatement(statement); 2554 closeResultSet(resultSet2); 2555 closeStatement(statement2); 2556 connection.close(); 2557 releaseSingleUserCaseReadLock(); 2558 } 2559 } 2560 2561 /** 2562 * Gets a specific data source for the case. If it is an image, an Image 2563 * will be instantiated. Otherwise, a LocalFilesDataSource will be 2564 * instantiated. 2565 * 2566 * NOTE: The DataSource class is an emerging feature and at present is only 2567 * useful for obtaining the object id and the data source identifier, an 2568 * ASCII-printable identifier for the data source that is intended to be 2569 * unique across multiple cases (e.g., a UUID). In the future, this method 2570 * will be a replacement for the getRootObjects method. 2571 * 2572 * @param objectId The object id of the data source. 2573 * 2574 * @return The data source. 2575 * 2576 * @throws TskDataException If there is no data source for the given object 2577 * id. 2578 * @throws TskCoreException If there is a problem getting the data source. 2579 */ getDataSource(long objectId)2580 public DataSource getDataSource(long objectId) throws TskDataException, TskCoreException { 2581 DataSource dataSource = null; 2582 CaseDbConnection connection = connections.getConnection(); 2583 acquireSingleUserCaseReadLock(); 2584 Statement statement = null; 2585 ResultSet resultSet = null; 2586 Statement statement2 = null; 2587 ResultSet resultSet2 = null; 2588 try { 2589 statement = connection.createStatement(); 2590 statement2 = connection.createStatement(); 2591 resultSet = connection.executeQuery(statement, 2592 "SELECT ds.device_id, ds.time_zone, img.type, img.ssize, img.size, img.md5, img.sha1, img.sha256, img.display_name " 2593 + "FROM data_source_info AS ds " 2594 + "LEFT JOIN tsk_image_info AS img " 2595 + "ON ds.obj_id = img.obj_id " 2596 + "WHERE ds.obj_id = " + objectId); //NON-NLS 2597 if (resultSet.next()) { 2598 String deviceId = resultSet.getString("device_id"); 2599 String timezone = resultSet.getString("time_zone"); 2600 String type = resultSet.getString("type"); 2601 2602 if (type == null) { 2603 /* 2604 * No data found in 'tsk_image_info', so we build an 2605 * LocalFilesDataSource. 2606 */ 2607 2608 resultSet2 = connection.executeQuery(statement2, "SELECT name FROM tsk_files WHERE tsk_files.obj_id = " + objectId); //NON-NLS 2609 String dsName = (resultSet2.next()) ? resultSet2.getString("name") : ""; 2610 2611 TSK_FS_NAME_TYPE_ENUM dirType = TSK_FS_NAME_TYPE_ENUM.DIR; 2612 TSK_FS_META_TYPE_ENUM metaType = TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR; 2613 TSK_FS_NAME_FLAG_ENUM dirFlag = TSK_FS_NAME_FLAG_ENUM.ALLOC; 2614 final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue() 2615 | TSK_FS_META_FLAG_ENUM.USED.getValue()); 2616 String parentPath = "/"; //NON-NLS 2617 dataSource = new LocalFilesDataSource(this, objectId, objectId, deviceId, dsName, dirType, metaType, dirFlag, metaFlags, timezone, null, FileKnown.UNKNOWN, parentPath); 2618 } else { 2619 /* 2620 * Data found in 'tsk_image_info', so we build an Image. 2621 */ 2622 Long ssize = resultSet.getLong("ssize"); 2623 Long size = resultSet.getLong("size"); 2624 String md5 = resultSet.getString("md5"); 2625 String sha1 = resultSet.getString("sha1"); 2626 String sha256 = resultSet.getString("sha256"); 2627 String name = resultSet.getString("display_name"); 2628 2629 List<String> imagePaths = getImagePathsById(objectId); 2630 if (name == null) { 2631 if (imagePaths.size() > 0) { 2632 String path = imagePaths.get(0); 2633 name = (new java.io.File(path)).getName(); 2634 } else { 2635 name = ""; 2636 } 2637 } 2638 2639 dataSource = new Image(this, objectId, Long.valueOf(type), deviceId, ssize, name, 2640 imagePaths.toArray(new String[imagePaths.size()]), timezone, md5, sha1, sha256, size); 2641 } 2642 } else { 2643 throw new TskDataException(String.format("There is no data source with obj_id = %d", objectId)); 2644 } 2645 } catch (SQLException ex) { 2646 throw new TskCoreException(String.format("Error getting data source with obj_id = %d", objectId), ex); 2647 } finally { 2648 closeResultSet(resultSet); 2649 closeStatement(statement); 2650 closeResultSet(resultSet2); 2651 closeStatement(statement2); 2652 connection.close(); 2653 releaseSingleUserCaseReadLock(); 2654 } 2655 2656 return dataSource; 2657 } 2658 2659 /** 2660 * Get all blackboard artifacts of a given type. Does not included rejected 2661 * artifacts. 2662 * 2663 * @param artifactTypeID artifact type id (must exist in database) 2664 * 2665 * @return list of blackboard artifacts. 2666 * 2667 * @throws TskCoreException 2668 */ getBlackboardArtifacts(int artifactTypeID)2669 public ArrayList<BlackboardArtifact> getBlackboardArtifacts(int artifactTypeID) throws TskCoreException { 2670 return getArtifactsHelper("blackboard_artifacts.artifact_type_id = " + artifactTypeID); 2671 } 2672 2673 /** 2674 * Get a count of blackboard artifacts for a given content. Does not include 2675 * rejected artifacts. 2676 * 2677 * @param objId Id of the content. 2678 * 2679 * @return The artifacts count for the content. 2680 * 2681 * @throws TskCoreException 2682 */ getBlackboardArtifactsCount(long objId)2683 public long getBlackboardArtifactsCount(long objId) throws TskCoreException { 2684 CaseDbConnection connection = connections.getConnection(); 2685 acquireSingleUserCaseReadLock(); 2686 ResultSet rs = null; 2687 try { 2688 // SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE obj_id = ? 2689 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_FROM_SOURCE); 2690 statement.clearParameters(); 2691 statement.setLong(1, objId); 2692 rs = connection.executeQuery(statement); 2693 long count = 0; 2694 if (rs.next()) { 2695 count = rs.getLong("count"); 2696 } 2697 return count; 2698 } catch (SQLException ex) { 2699 throw new TskCoreException("Error getting number of blackboard artifacts by content", ex); 2700 } finally { 2701 closeResultSet(rs); 2702 connection.close(); 2703 releaseSingleUserCaseReadLock(); 2704 } 2705 } 2706 2707 /** 2708 * Get a count of artifacts of a given type. Does not include rejected 2709 * artifacts. 2710 * 2711 * @param artifactTypeID Id of the artifact type. 2712 * 2713 * @return The artifacts count for the type. 2714 * 2715 * @throws TskCoreException 2716 */ getBlackboardArtifactsTypeCount(int artifactTypeID)2717 public long getBlackboardArtifactsTypeCount(int artifactTypeID) throws TskCoreException { 2718 CaseDbConnection connection = connections.getConnection(); 2719 acquireSingleUserCaseReadLock(); 2720 ResultSet rs = null; 2721 try { 2722 // SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE artifact_type_id = ? 2723 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_OF_TYPE); 2724 statement.clearParameters(); 2725 statement.setInt(1, artifactTypeID); 2726 rs = connection.executeQuery(statement); 2727 long count = 0; 2728 if (rs.next()) { 2729 count = rs.getLong("count"); 2730 } 2731 return count; 2732 } catch (SQLException ex) { 2733 throw new TskCoreException("Error getting number of blackboard artifacts by type", ex); 2734 } finally { 2735 closeResultSet(rs); 2736 connection.close(); 2737 releaseSingleUserCaseReadLock(); 2738 } 2739 } 2740 2741 /** 2742 * Get all blackboard artifacts that have an attribute of the given type and 2743 * String value. Does not included rejected artifacts. 2744 * 2745 * @param attrType attribute of this attribute type to look for in the 2746 * artifacts 2747 * @param value value of the attribute of the attrType type to look for 2748 * 2749 * @return a list of blackboard artifacts with such an attribute 2750 * 2751 * @throws TskCoreException exception thrown if a critical error occurred 2752 * within tsk core and artifacts could not be 2753 * queried 2754 */ getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, String value)2755 public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, String value) throws TskCoreException { 2756 CaseDbConnection connection = connections.getConnection(); 2757 acquireSingleUserCaseReadLock(); 2758 Statement s = null; 2759 ResultSet rs = null; 2760 try { 2761 s = connection.createStatement(); 2762 rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS 2763 + "arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " 2764 + "types.type_name AS type_name, types.display_name AS display_name, "//NON-NLS 2765 + " arts.review_status_id AS review_status_id " //NON-NLS 2766 + "FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS 2767 + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS 2768 + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS 2769 + " AND attrs.value_text = '" + value + "'" 2770 + " AND types.artifact_type_id=arts.artifact_type_id" 2771 + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); //NON-NLS 2772 ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); 2773 while (rs.next()) { 2774 artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"), 2775 rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), 2776 BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); 2777 } 2778 return artifacts; 2779 } catch (SQLException ex) { 2780 throw new TskCoreException("Error getting blackboard artifacts by attribute", ex); 2781 } finally { 2782 closeResultSet(rs); 2783 closeStatement(s); 2784 connection.close(); 2785 releaseSingleUserCaseReadLock(); 2786 } 2787 } 2788 2789 /** 2790 * Get all blackboard artifacts that have an attribute of the given type and 2791 * String value. Does not included rejected artifacts. 2792 * 2793 * @param attrType attribute of this attribute type to look for in the 2794 * artifacts 2795 * @param subString value substring of the string attribute of the attrType 2796 * type to look for 2797 * @param startsWith if true, the artifact attribute string should start 2798 * with the substring, if false, it should just contain it 2799 * 2800 * @return a list of blackboard artifacts with such an attribute 2801 * 2802 * @throws TskCoreException exception thrown if a critical error occurred 2803 * within tsk core and artifacts could not be 2804 * queried 2805 */ getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, String subString, boolean startsWith)2806 public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, String subString, boolean startsWith) throws TskCoreException { 2807 String valSubStr = "%" + subString; //NON-NLS 2808 if (startsWith == false) { 2809 valSubStr += "%"; //NON-NLS 2810 } 2811 CaseDbConnection connection = connections.getConnection(); 2812 acquireSingleUserCaseReadLock(); 2813 Statement s = null; 2814 ResultSet rs = null; 2815 try { 2816 s = connection.createStatement(); 2817 rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS 2818 + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " //NON-NLS 2819 + " types.type_name AS type_name, types.display_name AS display_name, " //NON-NLS 2820 + " arts.review_status_id AS review_status_id " //NON-NLS 2821 + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS 2822 + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS 2823 + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS 2824 + " AND LOWER(attrs.value_text) LIKE LOWER('" + valSubStr + "')" 2825 + " AND types.artifact_type_id=arts.artifact_type_id " 2826 + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); 2827 ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); 2828 while (rs.next()) { 2829 artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"), 2830 rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), 2831 BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); 2832 } 2833 return artifacts; 2834 } catch (SQLException ex) { 2835 throw new TskCoreException("Error getting blackboard artifacts by attribute. " + ex.getMessage(), ex); 2836 } finally { 2837 closeResultSet(rs); 2838 closeStatement(s); 2839 connection.close(); 2840 releaseSingleUserCaseReadLock(); 2841 } 2842 } 2843 2844 /** 2845 * Get all blackboard artifacts that have an attribute of the given type and 2846 * integer value. Does not included rejected artifacts. 2847 * 2848 * @param attrType attribute of this attribute type to look for in the 2849 * artifacts 2850 * @param value value of the attribute of the attrType type to look for 2851 * 2852 * @return a list of blackboard artifacts with such an attribute 2853 * 2854 * @throws TskCoreException exception thrown if a critical error occurred 2855 * within tsk core and artifacts could not be 2856 * queried 2857 */ getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, int value)2858 public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, int value) throws TskCoreException { 2859 CaseDbConnection connection = connections.getConnection(); 2860 acquireSingleUserCaseReadLock(); 2861 Statement s = null; 2862 ResultSet rs = null; 2863 try { 2864 s = connection.createStatement(); 2865 rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS 2866 + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " 2867 + " types.type_name AS type_name, types.display_name AS display_name, " 2868 + " arts.review_status_id AS review_status_id "//NON-NLS 2869 + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS 2870 + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS 2871 + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS 2872 + " AND attrs.value_int32 = " + value //NON-NLS 2873 + " AND types.artifact_type_id=arts.artifact_type_id " 2874 + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); 2875 ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); 2876 while (rs.next()) { 2877 artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"), 2878 rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), 2879 BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); 2880 } 2881 return artifacts; 2882 } catch (SQLException ex) { 2883 throw new TskCoreException("Error getting blackboard artifacts by attribute", ex); 2884 } finally { 2885 closeResultSet(rs); 2886 closeStatement(s); 2887 connection.close(); 2888 releaseSingleUserCaseReadLock(); 2889 } 2890 } 2891 2892 /** 2893 * Get all blackboard artifacts that have an attribute of the given type and 2894 * long value. Does not included rejected artifacts. 2895 * 2896 * @param attrType attribute of this attribute type to look for in the 2897 * artifacts 2898 * @param value value of the attribute of the attrType type to look for 2899 * 2900 * @return a list of blackboard artifacts with such an attribute 2901 * 2902 * @throws TskCoreException exception thrown if a critical error occurred 2903 * within tsk core and artifacts could not be 2904 * queried 2905 */ getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, long value)2906 public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, long value) throws TskCoreException { 2907 CaseDbConnection connection = connections.getConnection(); 2908 acquireSingleUserCaseReadLock(); 2909 Statement s = null; 2910 ResultSet rs = null; 2911 try { 2912 s = connection.createStatement(); 2913 rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS 2914 + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " 2915 + " types.type_name AS type_name, types.display_name AS display_name, " 2916 + " arts.review_status_id AS review_status_id "//NON-NLS 2917 + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS 2918 + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS 2919 + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS 2920 + " AND attrs.value_int64 = " + value //NON-NLS 2921 + " AND types.artifact_type_id=arts.artifact_type_id " 2922 + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); 2923 ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); 2924 while (rs.next()) { 2925 artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"), 2926 rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), 2927 BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); 2928 } 2929 return artifacts; 2930 } catch (SQLException ex) { 2931 throw new TskCoreException("Error getting blackboard artifacts by attribute. " + ex.getMessage(), ex); 2932 } finally { 2933 closeResultSet(rs); 2934 closeStatement(s); 2935 connection.close(); 2936 releaseSingleUserCaseReadLock(); 2937 } 2938 } 2939 2940 /** 2941 * Get all blackboard artifacts that have an attribute of the given type and 2942 * double value. Does not included rejected artifacts. 2943 * 2944 * @param attrType attribute of this attribute type to look for in the 2945 * artifacts 2946 * @param value value of the attribute of the attrType type to look for 2947 * 2948 * @return a list of blackboard artifacts with such an attribute 2949 * 2950 * @throws TskCoreException exception thrown if a critical error occurred 2951 * within tsk core and artifacts could not be 2952 * queried 2953 */ getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, double value)2954 public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, double value) throws TskCoreException { 2955 CaseDbConnection connection = connections.getConnection(); 2956 acquireSingleUserCaseReadLock(); 2957 Statement s = null; 2958 ResultSet rs = null; 2959 try { 2960 s = connection.createStatement(); 2961 rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS 2962 + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " 2963 + " types.type_name AS type_name, types.display_name AS display_name, " 2964 + " arts.review_status_id AS review_status_id "//NON-NLS 2965 + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS 2966 + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS 2967 + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS 2968 + " AND attrs.value_double = " + value //NON-NLS 2969 + " AND types.artifact_type_id=arts.artifact_type_id " 2970 + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); 2971 ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); 2972 while (rs.next()) { 2973 artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"), 2974 rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), 2975 BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); 2976 } 2977 return artifacts; 2978 } catch (SQLException ex) { 2979 throw new TskCoreException("Error getting blackboard artifacts by attribute", ex); 2980 } finally { 2981 closeResultSet(rs); 2982 closeStatement(s); 2983 connection.close(); 2984 releaseSingleUserCaseReadLock(); 2985 } 2986 } 2987 2988 /** 2989 * Get all blackboard artifacts that have an attribute of the given type and 2990 * byte value. Does not include rejected artifacts. 2991 * 2992 * @param attrType attribute of this attribute type to look for in the 2993 * artifacts 2994 * @param value value of the attribute of the attrType type to look for 2995 * 2996 * @return a list of blackboard artifacts with such an attribute 2997 * 2998 * @throws TskCoreException exception thrown if a critical error occurred 2999 * within tsk core and artifacts could not be 3000 * queried 3001 */ getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, byte value)3002 public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, byte value) throws TskCoreException { 3003 CaseDbConnection connection = connections.getConnection(); 3004 acquireSingleUserCaseReadLock(); 3005 Statement s = null; 3006 ResultSet rs = null; 3007 try { 3008 s = connection.createStatement(); 3009 rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS 3010 + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " 3011 + " types.type_name AS type_name, types.display_name AS display_name, " 3012 + " arts.review_status_id AS review_status_id "//NON-NLS 3013 + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS 3014 + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS 3015 + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS 3016 + " AND attrs.value_byte = " + value //NON-NLS 3017 + " AND types.artifact_type_id=arts.artifact_type_id " 3018 + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); 3019 ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); 3020 while (rs.next()) { 3021 artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"), 3022 rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), 3023 BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); 3024 } 3025 return artifacts; 3026 } catch (SQLException ex) { 3027 throw new TskCoreException("Error getting blackboard artifacts by attribute", ex); 3028 } finally { 3029 closeResultSet(rs); 3030 closeStatement(s); 3031 connection.close(); 3032 releaseSingleUserCaseReadLock(); 3033 } 3034 } 3035 3036 /** 3037 * Gets a list of all the artifact types for this case 3038 * 3039 * @return a list of artifact types 3040 * 3041 * @throws TskCoreException when there is an error getting the types 3042 */ getArtifactTypes()3043 public Iterable<BlackboardArtifact.Type> getArtifactTypes() throws TskCoreException { 3044 CaseDbConnection connection = connections.getConnection(); 3045 acquireSingleUserCaseReadLock(); 3046 Statement s = null; 3047 ResultSet rs = null; 3048 try { 3049 s = connection.createStatement(); 3050 rs = connection.executeQuery(s, "SELECT artifact_type_id, type_name, display_name FROM blackboard_artifact_types"); //NON-NLS 3051 ArrayList<BlackboardArtifact.Type> artifactTypes = new ArrayList<BlackboardArtifact.Type>(); 3052 while (rs.next()) { 3053 artifactTypes.add(new BlackboardArtifact.Type(rs.getInt("artifact_type_id"), 3054 rs.getString("type_name"), rs.getString("display_name"))); 3055 } 3056 return artifactTypes; 3057 } catch (SQLException ex) { 3058 throw new TskCoreException("Error getting artifact types", ex); //NON-NLS 3059 } finally { 3060 closeResultSet(rs); 3061 closeStatement(s); 3062 connection.close(); 3063 releaseSingleUserCaseReadLock(); 3064 } 3065 } 3066 3067 /** 3068 * Get all of the standard blackboard artifact types that are in use in the 3069 * blackboard. 3070 * 3071 * @return List of standard blackboard artifact types 3072 * 3073 * @throws TskCoreException 3074 */ getBlackboardArtifactTypesInUse()3075 public ArrayList<BlackboardArtifact.ARTIFACT_TYPE> getBlackboardArtifactTypesInUse() throws TskCoreException { 3076 String typeIdList = ""; 3077 for (int i = 0; i < BlackboardArtifact.ARTIFACT_TYPE.values().length; ++i) { 3078 typeIdList += BlackboardArtifact.ARTIFACT_TYPE.values()[i].getTypeID(); 3079 if (i < BlackboardArtifact.ARTIFACT_TYPE.values().length - 1) { 3080 typeIdList += ", "; 3081 } 3082 } 3083 String query = "SELECT DISTINCT artifact_type_id FROM blackboard_artifacts " 3084 + "WHERE artifact_type_id IN (" + typeIdList + ")"; 3085 CaseDbConnection connection = connections.getConnection(); 3086 acquireSingleUserCaseReadLock(); 3087 Statement s = null; 3088 ResultSet rs = null; 3089 try { 3090 s = connection.createStatement(); 3091 rs = connection.executeQuery(s, query); 3092 ArrayList<BlackboardArtifact.ARTIFACT_TYPE> usedArts = new ArrayList<BlackboardArtifact.ARTIFACT_TYPE>(); 3093 while (rs.next()) { 3094 usedArts.add(ARTIFACT_TYPE.fromID(rs.getInt("artifact_type_id"))); 3095 } 3096 return usedArts; 3097 } catch (SQLException ex) { 3098 throw new TskCoreException("Error getting artifact types in use", ex); 3099 } finally { 3100 closeResultSet(rs); 3101 closeStatement(s); 3102 connection.close(); 3103 releaseSingleUserCaseReadLock(); 3104 } 3105 } 3106 3107 /** 3108 * Gets the list of all unique artifact IDs in use. 3109 * 3110 * Gets both static and dynamic IDs. 3111 * 3112 * @return The list of unique IDs 3113 * 3114 * @throws TskCoreException exception thrown if a critical error occurred 3115 * within tsk core 3116 */ getArtifactTypesInUse()3117 public List<BlackboardArtifact.Type> getArtifactTypesInUse() throws TskCoreException { 3118 CaseDbConnection connection = connections.getConnection(); 3119 acquireSingleUserCaseReadLock(); 3120 Statement s = null; 3121 ResultSet rs = null; 3122 try { 3123 s = connection.createStatement(); 3124 rs = connection.executeQuery(s, 3125 "SELECT DISTINCT arts.artifact_type_id AS artifact_type_id, " 3126 + "types.type_name AS type_name, types.display_name AS display_name " 3127 + "FROM blackboard_artifact_types AS types " 3128 + "INNER JOIN blackboard_artifacts AS arts " 3129 + "ON arts.artifact_type_id = types.artifact_type_id"); //NON-NLS 3130 List<BlackboardArtifact.Type> uniqueArtifactTypes = new ArrayList<BlackboardArtifact.Type>(); 3131 while (rs.next()) { 3132 uniqueArtifactTypes.add(new BlackboardArtifact.Type(rs.getInt("artifact_type_id"), 3133 rs.getString("type_name"), rs.getString("display_name"))); 3134 } 3135 return uniqueArtifactTypes; 3136 } catch (SQLException ex) { 3137 throw new TskCoreException("Error getting attribute types", ex); 3138 } finally { 3139 closeResultSet(rs); 3140 closeStatement(s); 3141 connection.close(); 3142 releaseSingleUserCaseReadLock(); 3143 } 3144 } 3145 3146 /** 3147 * Gets a list of all the attribute types for this case 3148 * 3149 * @return a list of attribute types 3150 * 3151 * @throws TskCoreException when there is an error getting the types 3152 */ getAttributeTypes()3153 public List<BlackboardAttribute.Type> getAttributeTypes() throws TskCoreException { 3154 CaseDbConnection connection = connections.getConnection(); 3155 acquireSingleUserCaseReadLock(); 3156 Statement s = null; 3157 ResultSet rs = null; 3158 try { 3159 s = connection.createStatement(); 3160 rs = connection.executeQuery(s, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types"); //NON-NLS 3161 ArrayList<BlackboardAttribute.Type> attribute_types = new ArrayList<BlackboardAttribute.Type>(); 3162 while (rs.next()) { 3163 attribute_types.add(new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"), 3164 rs.getString("display_name"), TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type")))); 3165 } 3166 return attribute_types; 3167 } catch (SQLException ex) { 3168 throw new TskCoreException("Error getting attribute types", ex); 3169 } finally { 3170 closeResultSet(rs); 3171 closeStatement(s); 3172 connection.close(); 3173 releaseSingleUserCaseReadLock(); 3174 } 3175 } 3176 3177 /** 3178 * Get count of blackboard attribute types 3179 * 3180 * Counts both static (in enum) and dynamic attributes types (created by 3181 * modules at runtime) 3182 * 3183 * @return count of attribute types 3184 * 3185 * @throws TskCoreException exception thrown if a critical error occurs 3186 * within TSK core 3187 */ getBlackboardAttributeTypesCount()3188 public int getBlackboardAttributeTypesCount() throws TskCoreException { 3189 CaseDbConnection connection = connections.getConnection(); 3190 acquireSingleUserCaseReadLock(); 3191 Statement s = null; 3192 ResultSet rs = null; 3193 try { 3194 s = connection.createStatement(); 3195 rs = connection.executeQuery(s, "SELECT COUNT(*) AS count FROM blackboard_attribute_types"); //NON-NLS 3196 int count = 0; 3197 if (rs.next()) { 3198 count = rs.getInt("count"); 3199 } 3200 return count; 3201 } catch (SQLException ex) { 3202 throw new TskCoreException("Error getting number of blackboard artifacts by type", ex); 3203 } finally { 3204 closeResultSet(rs); 3205 closeStatement(s); 3206 connection.close(); 3207 releaseSingleUserCaseReadLock(); 3208 } 3209 } 3210 3211 /** 3212 * Gets unrejected blackboard artifacts that match a given WHERE clause. 3213 * Uses a SELECT * statement that does a join of the blackboard_artifacts 3214 * and blackboard_artifact_types tables to get all of the required data. 3215 * 3216 * @param whereClause The WHERE clause to append to the SELECT statement. 3217 * 3218 * @return A list of BlackboardArtifact objects. 3219 * 3220 * @throws TskCoreException If there is a problem querying the case 3221 * database. 3222 */ getArtifactsHelper(String whereClause)3223 ArrayList<BlackboardArtifact> getArtifactsHelper(String whereClause) throws TskCoreException { 3224 CaseDbConnection connection = connections.getConnection(); 3225 acquireSingleUserCaseReadLock(); 3226 ResultSet rs = null; 3227 try { 3228 Statement statement = connection.createStatement(); 3229 String query = "SELECT blackboard_artifacts.artifact_id AS artifact_id, " 3230 + "blackboard_artifacts.obj_id AS obj_id, " 3231 + "blackboard_artifacts.artifact_obj_id AS artifact_obj_id, " 3232 + "blackboard_artifacts.data_source_obj_id AS data_source_obj_id, " 3233 + "blackboard_artifact_types.artifact_type_id AS artifact_type_id, " 3234 + "blackboard_artifact_types.type_name AS type_name, " 3235 + "blackboard_artifact_types.display_name AS display_name, " 3236 + "blackboard_artifacts.review_status_id AS review_status_id " 3237 + "FROM blackboard_artifacts, blackboard_artifact_types " 3238 + "WHERE blackboard_artifacts.artifact_type_id = blackboard_artifact_types.artifact_type_id " 3239 + " AND blackboard_artifacts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID() 3240 + " AND " + whereClause; 3241 rs = connection.executeQuery(statement, query); 3242 ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); 3243 while (rs.next()) { 3244 artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"), 3245 rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), 3246 BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); 3247 } 3248 return artifacts; 3249 } catch (SQLException ex) { 3250 throw new TskCoreException("Error getting or creating a blackboard artifact", ex); 3251 } finally { 3252 closeResultSet(rs); 3253 connection.close(); 3254 releaseSingleUserCaseReadLock(); 3255 } 3256 } 3257 3258 /** 3259 * Helper method to get count of all artifacts matching the type id and 3260 * object id. Does not included rejected artifacts. 3261 * 3262 * @param artifactTypeID artifact type id 3263 * @param obj_id associated object id 3264 * 3265 * @return count of matching blackboard artifacts 3266 * 3267 * @throws TskCoreException exception thrown if a critical error occurs 3268 * within TSK core 3269 */ getArtifactsCountHelper(int artifactTypeID, long obj_id)3270 private long getArtifactsCountHelper(int artifactTypeID, long obj_id) throws TskCoreException { 3271 CaseDbConnection connection = connections.getConnection(); 3272 acquireSingleUserCaseReadLock(); 3273 ResultSet rs = null; 3274 try { 3275 // SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE obj_id = ? AND artifact_type_id = ? 3276 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_BY_SOURCE_AND_TYPE); 3277 statement.clearParameters(); 3278 statement.setLong(1, obj_id); 3279 statement.setInt(2, artifactTypeID); 3280 rs = connection.executeQuery(statement); 3281 long count = 0; 3282 if (rs.next()) { 3283 count = rs.getLong("count"); 3284 } 3285 return count; 3286 } catch (SQLException ex) { 3287 throw new TskCoreException("Error getting blackboard artifact count", ex); 3288 } finally { 3289 closeResultSet(rs); 3290 connection.close(); 3291 releaseSingleUserCaseReadLock(); 3292 } 3293 } 3294 3295 /** 3296 * Get all blackboard artifacts of a given type for the given object id. 3297 * Does not included rejected artifacts. 3298 * 3299 * @param artifactTypeName artifact type name 3300 * @param obj_id object id 3301 * 3302 * @return list of blackboard artifacts 3303 * 3304 * @throws TskCoreException exception thrown if a critical error occurs 3305 * within TSK core 3306 */ getBlackboardArtifacts(String artifactTypeName, long obj_id)3307 public ArrayList<BlackboardArtifact> getBlackboardArtifacts(String artifactTypeName, long obj_id) throws TskCoreException { 3308 return getArtifactsHelper("blackboard_artifacts.obj_id = " + obj_id + " AND blackboard_artifact_types.type_name = '" + artifactTypeName + "';"); 3309 } 3310 3311 /** 3312 * Get all blackboard artifacts of a given type for the given object id. 3313 * Does not included rejected artifacts. 3314 * 3315 * @param artifactTypeID artifact type id (must exist in database) 3316 * @param obj_id object id 3317 * 3318 * @return list of blackboard artifacts 3319 * 3320 * @throws TskCoreException exception thrown if a critical error occurs 3321 * within TSK core 3322 */ getBlackboardArtifacts(int artifactTypeID, long obj_id)3323 public ArrayList<BlackboardArtifact> getBlackboardArtifacts(int artifactTypeID, long obj_id) throws TskCoreException { 3324 return getArtifactsHelper("blackboard_artifacts.obj_id = " + obj_id + " AND blackboard_artifact_types.artifact_type_id = " + artifactTypeID + ";"); 3325 } 3326 3327 /** 3328 * Get all blackboard artifacts of a given type for the given object id. 3329 * Does not included rejected artifacts. 3330 * 3331 * @param artifactType artifact type enum 3332 * @param obj_id object id 3333 * 3334 * @return list of blackboard artifacts 3335 * 3336 * @throws TskCoreException exception thrown if a critical error occurs 3337 * within TSK core 3338 */ getBlackboardArtifacts(ARTIFACT_TYPE artifactType, long obj_id)3339 public ArrayList<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactType, long obj_id) throws TskCoreException { 3340 return getBlackboardArtifacts(artifactType.getTypeID(), obj_id); 3341 } 3342 3343 /** 3344 * Get count of all blackboard artifacts of a given type for the given 3345 * object id. Does not include rejected artifacts. 3346 * 3347 * @param artifactTypeName artifact type name 3348 * @param obj_id object id 3349 * 3350 * @return count of blackboard artifacts 3351 * 3352 * @throws TskCoreException exception thrown if a critical error occurs 3353 * within TSK core 3354 */ getBlackboardArtifactsCount(String artifactTypeName, long obj_id)3355 public long getBlackboardArtifactsCount(String artifactTypeName, long obj_id) throws TskCoreException { 3356 int artifactTypeID = this.getArtifactType(artifactTypeName).getTypeID(); 3357 if (artifactTypeID == -1) { 3358 return 0; 3359 } 3360 return getArtifactsCountHelper(artifactTypeID, obj_id); 3361 } 3362 3363 /** 3364 * Get count of all blackboard artifacts of a given type for the given 3365 * object id. Does not include rejected artifacts. 3366 * 3367 * @param artifactTypeID artifact type id (must exist in database) 3368 * @param obj_id object id 3369 * 3370 * @return count of blackboard artifacts 3371 * 3372 * @throws TskCoreException exception thrown if a critical error occurs 3373 * within TSK core 3374 */ getBlackboardArtifactsCount(int artifactTypeID, long obj_id)3375 public long getBlackboardArtifactsCount(int artifactTypeID, long obj_id) throws TskCoreException { 3376 return getArtifactsCountHelper(artifactTypeID, obj_id); 3377 } 3378 3379 /** 3380 * Get count of all blackboard artifacts of a given type for the given 3381 * object id. Does not include rejected artifacts. 3382 * 3383 * @param artifactType artifact type enum 3384 * @param obj_id object id 3385 * 3386 * @return count of blackboard artifacts 3387 * 3388 * @throws TskCoreException exception thrown if a critical error occurs 3389 * within TSK core 3390 */ getBlackboardArtifactsCount(ARTIFACT_TYPE artifactType, long obj_id)3391 public long getBlackboardArtifactsCount(ARTIFACT_TYPE artifactType, long obj_id) throws TskCoreException { 3392 return getArtifactsCountHelper(artifactType.getTypeID(), obj_id); 3393 } 3394 3395 /** 3396 * Get all blackboard artifacts of a given type. Does not included rejected 3397 * artifacts. 3398 * 3399 * @param artifactTypeName artifact type name 3400 * 3401 * @return list of blackboard artifacts 3402 * 3403 * @throws TskCoreException exception thrown if a critical error occurs 3404 * within TSK core 3405 */ getBlackboardArtifacts(String artifactTypeName)3406 public ArrayList<BlackboardArtifact> getBlackboardArtifacts(String artifactTypeName) throws TskCoreException { 3407 return getArtifactsHelper("blackboard_artifact_types.type_name = '" + artifactTypeName + "';"); 3408 } 3409 3410 /** 3411 * Get all blackboard artifacts of a given type. Does not included rejected 3412 * artifacts. 3413 * 3414 * @param artifactType artifact type enum 3415 * 3416 * @return list of blackboard artifacts 3417 * 3418 * @throws TskCoreException exception thrown if a critical error occurs 3419 * within TSK core 3420 */ getBlackboardArtifacts(ARTIFACT_TYPE artifactType)3421 public ArrayList<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactType) throws TskCoreException { 3422 return getArtifactsHelper("blackboard_artifact_types.artifact_type_id = " + artifactType.getTypeID() + ";"); 3423 } 3424 3425 /** 3426 * Get all blackboard artifacts of a given type with an attribute of a given 3427 * type and String value. Does not included rejected artifacts. 3428 * 3429 * @param artifactType artifact type enum 3430 * @param attrType attribute type enum 3431 * @param value String value of attribute 3432 * 3433 * @return list of blackboard artifacts 3434 * 3435 * @throws TskCoreException exception thrown if a critical error occurs 3436 * within TSK core 3437 */ getBlackboardArtifacts(ARTIFACT_TYPE artifactType, BlackboardAttribute.ATTRIBUTE_TYPE attrType, String value)3438 public List<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactType, BlackboardAttribute.ATTRIBUTE_TYPE attrType, String value) throws TskCoreException { 3439 CaseDbConnection connection = connections.getConnection(); 3440 acquireSingleUserCaseReadLock(); 3441 Statement s = null; 3442 ResultSet rs = null; 3443 try { 3444 s = connection.createStatement(); 3445 rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS 3446 + "arts.obj_id AS obj_id, arts.artifact_obj_id as artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " 3447 + "types.type_name AS type_name, types.display_name AS display_name," 3448 + "arts.review_status_id AS review_status_id "//NON-NLS 3449 + "FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS 3450 + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS 3451 + "AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS 3452 + " AND arts.artifact_type_id = " + artifactType.getTypeID() //NON-NLS 3453 + " AND attrs.value_text = '" + value + "'" //NON-NLS 3454 + " AND types.artifact_type_id=arts.artifact_type_id" 3455 + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); 3456 ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); 3457 while (rs.next()) { 3458 artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"), 3459 rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), 3460 BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); 3461 } 3462 return artifacts; 3463 } catch (SQLException ex) { 3464 throw new TskCoreException("Error getting blackboard artifacts by artifact type and attribute. " + ex.getMessage(), ex); 3465 } finally { 3466 closeResultSet(rs); 3467 closeStatement(s); 3468 connection.close(); 3469 releaseSingleUserCaseReadLock(); 3470 } 3471 } 3472 3473 /** 3474 * Get the blackboard artifact with the given artifact id 3475 * 3476 * @param artifactID artifact ID 3477 * 3478 * @return blackboard artifact 3479 * 3480 * @throws TskCoreException exception thrown if a critical error occurs 3481 * within TSK core 3482 */ getBlackboardArtifact(long artifactID)3483 public BlackboardArtifact getBlackboardArtifact(long artifactID) throws TskCoreException { 3484 CaseDbConnection connection = connections.getConnection(); 3485 acquireSingleUserCaseReadLock(); 3486 ResultSet rs = null; 3487 Statement s; 3488 try { 3489 s = connection.createStatement(); 3490 rs = connection.executeQuery(s, "SELECT arts.artifact_id AS artifact_id, " 3491 + "arts.obj_id AS obj_id, arts.artifact_obj_id as artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " 3492 + "types.type_name AS type_name, types.display_name AS display_name," 3493 + "arts.review_status_id AS review_status_id "//NON-NLS 3494 + "FROM blackboard_artifacts AS arts, blackboard_artifact_types AS types " 3495 + "WHERE arts.artifact_id = " + artifactID 3496 + " AND arts.artifact_type_id = types.artifact_type_id"); 3497 if (rs.next()) { 3498 return new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"), 3499 rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), 3500 BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))); 3501 } else { 3502 /* 3503 * I think this should actually return null (or Optional) when 3504 * there is no artifact with the given id, but it looks like 3505 * existing code is not expecting that. -jm 3506 */ 3507 throw new TskCoreException("No blackboard artifact with id " + artifactID); 3508 } 3509 } catch (SQLException ex) { 3510 throw new TskCoreException("Error getting a blackboard artifact. " + ex.getMessage(), ex); 3511 } finally { 3512 closeResultSet(rs); 3513 connection.close(); 3514 releaseSingleUserCaseReadLock(); 3515 } 3516 } 3517 3518 /** 3519 * Add a blackboard attribute. 3520 * 3521 * @param attr A blackboard attribute. 3522 * @param artifactTypeId The type of artifact associated with the attribute. 3523 * 3524 * @throws TskCoreException thrown if a critical error occurs. 3525 */ addBlackboardAttribute(BlackboardAttribute attr, int artifactTypeId)3526 public void addBlackboardAttribute(BlackboardAttribute attr, int artifactTypeId) throws TskCoreException { 3527 CaseDbConnection connection = connections.getConnection(); 3528 acquireSingleUserCaseWriteLock(); 3529 try { 3530 addBlackBoardAttribute(attr, artifactTypeId, connection); 3531 } catch (SQLException ex) { 3532 throw new TskCoreException("Error adding blackboard attribute " + attr.toString(), ex); 3533 } finally { 3534 connection.close(); 3535 releaseSingleUserCaseWriteLock(); 3536 } 3537 } 3538 3539 /** 3540 * Add a set blackboard attributes. 3541 * 3542 * @param attributes A set of blackboard attribute. 3543 * @param artifactTypeId The type of artifact associated with the 3544 * attributes. 3545 * 3546 * @throws TskCoreException thrown if a critical error occurs. 3547 */ addBlackboardAttributes(Collection<BlackboardAttribute> attributes, int artifactTypeId)3548 public void addBlackboardAttributes(Collection<BlackboardAttribute> attributes, int artifactTypeId) throws TskCoreException { 3549 CaseDbConnection connection = connections.getConnection(); 3550 acquireSingleUserCaseWriteLock(); 3551 try { 3552 connection.beginTransaction(); 3553 for (final BlackboardAttribute attr : attributes) { 3554 addBlackBoardAttribute(attr, artifactTypeId, connection); 3555 } 3556 connection.commitTransaction(); 3557 } catch (SQLException ex) { 3558 connection.rollbackTransaction(); 3559 throw new TskCoreException("Error adding blackboard attributes", ex); 3560 } finally { 3561 connection.close(); 3562 releaseSingleUserCaseWriteLock(); 3563 } 3564 } 3565 addBlackBoardAttribute(BlackboardAttribute attr, int artifactTypeId, CaseDbConnection connection)3566 private void addBlackBoardAttribute(BlackboardAttribute attr, int artifactTypeId, CaseDbConnection connection) throws SQLException, TskCoreException { 3567 PreparedStatement statement; 3568 switch (attr.getAttributeType().getValueType()) { 3569 case STRING: 3570 statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_STRING_ATTRIBUTE); 3571 statement.clearParameters(); 3572 statement.setString(7, attr.getValueString()); 3573 break; 3574 case BYTE: 3575 statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_BYTE_ATTRIBUTE); 3576 statement.clearParameters(); 3577 statement.setBytes(7, attr.getValueBytes()); 3578 break; 3579 case INTEGER: 3580 statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_INT_ATTRIBUTE); 3581 statement.clearParameters(); 3582 statement.setInt(7, attr.getValueInt()); 3583 break; 3584 case LONG: 3585 statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LONG_ATTRIBUTE); 3586 statement.clearParameters(); 3587 statement.setLong(7, attr.getValueLong()); 3588 break; 3589 case DOUBLE: 3590 statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_DOUBLE_ATTRIBUTE); 3591 statement.clearParameters(); 3592 statement.setDouble(7, attr.getValueDouble()); 3593 break; 3594 case DATETIME: 3595 statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LONG_ATTRIBUTE); 3596 statement.clearParameters(); 3597 statement.setLong(7, attr.getValueLong()); 3598 break; 3599 default: 3600 throw new TskCoreException("Unrecognized artifact attribute value type"); 3601 } 3602 statement.setLong(1, attr.getArtifactID()); 3603 statement.setInt(2, artifactTypeId); 3604 statement.setString(3, attr.getSourcesCSV()); 3605 statement.setString(4, ""); 3606 statement.setInt(5, attr.getAttributeType().getTypeID()); 3607 statement.setLong(6, attr.getAttributeType().getValueType().getType()); 3608 connection.executeUpdate(statement); 3609 } 3610 3611 /** 3612 * Adds a source name to the source column of one or more rows in the 3613 * blackboard attributes table. The source name will be added to a CSV list 3614 * in any rows that exactly match the attribute's artifact_id and value. 3615 * 3616 * @param attr The artifact attribute 3617 * @param source The source name. 3618 * 3619 * @throws TskCoreException 3620 */ addSourceToArtifactAttribute(BlackboardAttribute attr, String source)3621 String addSourceToArtifactAttribute(BlackboardAttribute attr, String source) throws TskCoreException { 3622 /* 3623 * WARNING: This is a temporary implementation that is not safe and 3624 * denormalizes the case datbase. 3625 * 3626 * TODO (JIRA-2294): Provide a safe and normalized solution to tracking 3627 * the sources of artifact attributes. 3628 */ 3629 if (null == source || source.isEmpty()) { 3630 throw new TskCoreException("Attempt to add null or empty source module name to artifact attribute"); 3631 } 3632 CaseDbConnection connection = connections.getConnection(); 3633 acquireSingleUserCaseWriteLock(); 3634 Statement queryStmt = null; 3635 Statement updateStmt = null; 3636 ResultSet result = null; 3637 String newSources = ""; 3638 try { 3639 connection.beginTransaction(); 3640 String valueClause = ""; 3641 BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType = attr.getAttributeType().getValueType(); 3642 if (BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE != valueType) { 3643 switch (valueType) { 3644 case STRING: 3645 valueClause = " value_text = '" + escapeSingleQuotes(attr.getValueString()) + "'"; 3646 break; 3647 case INTEGER: 3648 valueClause = " value_int32 = " + attr.getValueInt(); 3649 break; 3650 case LONG: 3651 case DATETIME: 3652 valueClause = " value_int64 = " + attr.getValueLong(); 3653 break; 3654 case DOUBLE: 3655 valueClause = " value_double = " + attr.getValueDouble(); 3656 break; 3657 default: 3658 throw new TskCoreException(String.format("Unrecognized value type for attribute %s", attr.getDisplayString())); 3659 } 3660 String query = "SELECT source FROM blackboard_attributes WHERE" 3661 + " artifact_id = " + attr.getArtifactID() 3662 + " AND attribute_type_id = " + attr.getAttributeType().getTypeID() 3663 + " AND value_type = " + attr.getAttributeType().getValueType().getType() 3664 + " AND " + valueClause + ";"; 3665 queryStmt = connection.createStatement(); 3666 updateStmt = connection.createStatement(); 3667 result = connection.executeQuery(queryStmt, query); 3668 } else { 3669 /* 3670 * SELECT source FROM blackboard_attributes WHERE artifact_id = 3671 * ? AND attribute_type_id = ? AND value_type = 4 AND value_byte 3672 * = ? 3673 */ 3674 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ATTR_BY_VALUE_BYTE); 3675 statement.clearParameters(); 3676 statement.setLong(1, attr.getArtifactID()); 3677 statement.setLong(2, attr.getAttributeType().getTypeID()); 3678 statement.setBytes(3, attr.getValueBytes()); 3679 result = connection.executeQuery(statement); 3680 } 3681 while (result.next()) { 3682 String oldSources = result.getString("source"); 3683 if (null != oldSources && !oldSources.isEmpty()) { 3684 Set<String> uniqueSources = new HashSet<String>(Arrays.asList(oldSources.split(","))); 3685 if (!uniqueSources.contains(source)) { 3686 newSources = oldSources + "," + source; 3687 } else { 3688 newSources = oldSources; 3689 } 3690 } else { 3691 newSources = source; 3692 } 3693 if (BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE != valueType) { 3694 String update = "UPDATE blackboard_attributes SET source = '" + newSources + "' WHERE" 3695 + " artifact_id = " + attr.getArtifactID() 3696 + " AND attribute_type_id = " + attr.getAttributeType().getTypeID() 3697 + " AND value_type = " + attr.getAttributeType().getValueType().getType() 3698 + " AND " + valueClause + ";"; 3699 connection.executeUpdate(updateStmt, update); 3700 } else { 3701 /* 3702 * UPDATE blackboard_attributes SET source = ? WHERE 3703 * artifact_id = ? AND attribute_type_id = ? AND value_type 3704 * = 4 AND value_byte = ? 3705 */ 3706 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.UPDATE_ATTR_BY_VALUE_BYTE); 3707 statement.clearParameters(); 3708 statement.setString(1, newSources); 3709 statement.setLong(2, attr.getArtifactID()); 3710 statement.setLong(3, attr.getAttributeType().getTypeID()); 3711 statement.setBytes(4, attr.getValueBytes()); 3712 connection.executeUpdate(statement); 3713 } 3714 } 3715 connection.commitTransaction(); 3716 return newSources; 3717 } catch (SQLException ex) { 3718 connection.rollbackTransaction(); 3719 throw new TskCoreException(String.format("Error adding source module to attribute %s", attr.getDisplayString()), ex); 3720 } finally { 3721 closeResultSet(result); 3722 closeStatement(updateStmt); 3723 closeStatement(queryStmt); 3724 connection.close(); 3725 releaseSingleUserCaseWriteLock(); 3726 } 3727 } 3728 3729 /** 3730 * Add an attribute type with the given name 3731 * 3732 * @param attrTypeString Name of the new attribute 3733 * @param valueType The value type of this new attribute type 3734 * @param displayName The (non-unique) display name of the attribute type 3735 * 3736 * @return the id of the new attribute 3737 * 3738 * @throws TskCoreException exception thrown if a critical error occurs 3739 * within tsk core 3740 * @throws TskDataException exception thrown if attribute type was already 3741 * in the system 3742 */ addArtifactAttributeType(String attrTypeString, TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName)3743 public BlackboardAttribute.Type addArtifactAttributeType(String attrTypeString, TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName) throws TskCoreException, TskDataException { 3744 CaseDbConnection connection = connections.getConnection(); 3745 acquireSingleUserCaseWriteLock(); 3746 Statement s = null; 3747 ResultSet rs = null; 3748 try { 3749 connection.beginTransaction(); 3750 s = connection.createStatement(); 3751 rs = connection.executeQuery(s, "SELECT attribute_type_id FROM blackboard_attribute_types WHERE type_name = '" + attrTypeString + "'"); //NON-NLS 3752 if (!rs.next()) { 3753 rs.close(); 3754 rs = connection.executeQuery(s, "SELECT MAX(attribute_type_id) AS highest_id FROM blackboard_attribute_types"); 3755 int maxID = 0; 3756 if (rs.next()) { 3757 maxID = rs.getInt("highest_id"); 3758 if (maxID < MIN_USER_DEFINED_TYPE_ID) { 3759 maxID = MIN_USER_DEFINED_TYPE_ID; 3760 } else { 3761 maxID++; 3762 } 3763 } 3764 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 3765 BlackboardAttribute.Type type = new BlackboardAttribute.Type(maxID, attrTypeString, displayName, valueType); 3766 this.typeIdToAttributeTypeMap.put(type.getTypeID(), type); 3767 this.typeNameToAttributeTypeMap.put(type.getTypeName(), type); 3768 connection.commitTransaction(); 3769 return type; 3770 } else { 3771 throw new TskDataException("The attribute type that was added was already within the system."); 3772 } 3773 3774 } catch (SQLException ex) { 3775 connection.rollbackTransaction(); 3776 throw new TskCoreException("Error adding attribute type", ex); 3777 } finally { 3778 closeResultSet(rs); 3779 closeStatement(s); 3780 connection.close(); 3781 releaseSingleUserCaseWriteLock(); 3782 } 3783 } 3784 3785 /** 3786 * Get the attribute type associated with an attribute type name. 3787 * 3788 * @param attrTypeName An attribute type name. 3789 * 3790 * @return An attribute type or null if the attribute type does not exist. 3791 * 3792 * @throws TskCoreException If an error occurs accessing the case database. 3793 * 3794 */ getAttributeType(String attrTypeName)3795 public BlackboardAttribute.Type getAttributeType(String attrTypeName) throws TskCoreException { 3796 if (this.typeNameToAttributeTypeMap.containsKey(attrTypeName)) { 3797 return this.typeNameToAttributeTypeMap.get(attrTypeName); 3798 } 3799 CaseDbConnection connection = connections.getConnection(); 3800 acquireSingleUserCaseReadLock(); 3801 Statement s = null; 3802 ResultSet rs = null; 3803 try { 3804 s = connection.createStatement(); 3805 rs = connection.executeQuery(s, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types WHERE type_name = '" + attrTypeName + "'"); //NON-NLS 3806 BlackboardAttribute.Type type = null; 3807 if (rs.next()) { 3808 type = new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"), 3809 rs.getString("display_name"), TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type"))); 3810 this.typeIdToAttributeTypeMap.put(type.getTypeID(), type); 3811 this.typeNameToAttributeTypeMap.put(attrTypeName, type); 3812 } 3813 return type; 3814 } catch (SQLException ex) { 3815 throw new TskCoreException("Error getting attribute type id", ex); 3816 } finally { 3817 closeResultSet(rs); 3818 closeStatement(s); 3819 connection.close(); 3820 releaseSingleUserCaseReadLock(); 3821 } 3822 } 3823 3824 /** 3825 * Get the attribute type associated with an attribute type ID. 3826 * 3827 * @param typeID An attribute type ID. 3828 * 3829 * @return An attribute type or null if the attribute type does not exist. 3830 * 3831 * @throws TskCoreException If an error occurs accessing the case database. 3832 * 3833 */ getAttributeType(int typeID)3834 private BlackboardAttribute.Type getAttributeType(int typeID) throws TskCoreException { 3835 if (this.typeIdToAttributeTypeMap.containsKey(typeID)) { 3836 return this.typeIdToAttributeTypeMap.get(typeID); 3837 } 3838 CaseDbConnection connection = connections.getConnection(); 3839 acquireSingleUserCaseReadLock(); 3840 Statement s = null; 3841 ResultSet rs = null; 3842 try { 3843 s = connection.createStatement(); 3844 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 3845 BlackboardAttribute.Type type = null; 3846 if (rs.next()) { 3847 type = new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"), 3848 rs.getString("display_name"), TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type"))); 3849 this.typeIdToAttributeTypeMap.put(typeID, type); 3850 this.typeNameToAttributeTypeMap.put(type.getTypeName(), type); 3851 } 3852 return type; 3853 } catch (SQLException ex) { 3854 throw new TskCoreException("Error getting attribute type id", ex); 3855 } finally { 3856 closeResultSet(rs); 3857 closeStatement(s); 3858 connection.close(); 3859 releaseSingleUserCaseReadLock(); 3860 } 3861 } 3862 3863 /** 3864 * Get the artifact type associated with an artifact type name. 3865 * 3866 * @param artTypeName An artifact type name. 3867 * 3868 * @return An artifact type or null if the artifact type does not exist. 3869 * 3870 * @throws TskCoreException If an error occurs accessing the case database. 3871 * 3872 */ getArtifactType(String artTypeName)3873 public BlackboardArtifact.Type getArtifactType(String artTypeName) throws TskCoreException { 3874 if (this.typeNameToArtifactTypeMap.containsKey(artTypeName)) { 3875 return this.typeNameToArtifactTypeMap.get(artTypeName); 3876 } 3877 CaseDbConnection connection = connections.getConnection(); 3878 acquireSingleUserCaseReadLock(); 3879 Statement s = null; 3880 ResultSet rs = null; 3881 try { 3882 s = connection.createStatement(); 3883 rs = connection.executeQuery(s, "SELECT artifact_type_id, type_name, display_name FROM blackboard_artifact_types WHERE type_name = '" + artTypeName + "'"); //NON-NLS 3884 BlackboardArtifact.Type type = null; 3885 if (rs.next()) { 3886 type = new BlackboardArtifact.Type(rs.getInt("artifact_type_id"), 3887 rs.getString("type_name"), rs.getString("display_name")); 3888 this.typeIdToArtifactTypeMap.put(type.getTypeID(), type); 3889 this.typeNameToArtifactTypeMap.put(artTypeName, type); 3890 } 3891 return type; 3892 } catch (SQLException ex) { 3893 throw new TskCoreException("Error getting artifact type from the database", ex); 3894 } finally { 3895 closeResultSet(rs); 3896 closeStatement(s); 3897 connection.close(); 3898 releaseSingleUserCaseReadLock(); 3899 } 3900 } 3901 3902 /** 3903 * Get the artifact type associated with an artifact type id. 3904 * 3905 * @param artTypeId An artifact type id. 3906 * 3907 * @return An artifact type or null if the artifact type does not exist. 3908 * 3909 * @throws TskCoreException If an error occurs accessing the case database. 3910 * 3911 */ getArtifactType(int artTypeId)3912 BlackboardArtifact.Type getArtifactType(int artTypeId) throws TskCoreException { 3913 if (this.typeIdToArtifactTypeMap.containsKey(artTypeId)) { 3914 return typeIdToArtifactTypeMap.get(artTypeId); 3915 } 3916 CaseDbConnection connection = connections.getConnection(); 3917 acquireSingleUserCaseReadLock(); 3918 Statement s = null; 3919 ResultSet rs = null; 3920 try { 3921 s = connection.createStatement(); 3922 rs = connection.executeQuery(s, "SELECT artifact_type_id, type_name, display_name FROM blackboard_artifact_types WHERE artifact_type_id = " + artTypeId + ""); //NON-NLS 3923 BlackboardArtifact.Type type = null; 3924 if (rs.next()) { 3925 type = new BlackboardArtifact.Type(rs.getInt("artifact_type_id"), 3926 rs.getString("type_name"), rs.getString("display_name")); 3927 this.typeIdToArtifactTypeMap.put(artTypeId, type); 3928 this.typeNameToArtifactTypeMap.put(type.getTypeName(), type); 3929 } 3930 return type; 3931 } catch (SQLException ex) { 3932 throw new TskCoreException("Error getting artifact type from the database", ex); 3933 } finally { 3934 closeResultSet(rs); 3935 closeStatement(s); 3936 connection.close(); 3937 releaseSingleUserCaseReadLock(); 3938 } 3939 } 3940 3941 /** 3942 * Add an artifact type with the given name. Will return an artifact Type. 3943 * 3944 * @param artifactTypeName System (unique) name of artifact 3945 * @param displayName Display (non-unique) name of artifact 3946 * 3947 * @return Type of the artifact added 3948 * 3949 * @throws TskCoreException exception thrown if a critical error occurs 3950 * @throws TskDataException exception thrown if given data is already in db 3951 * within tsk core 3952 */ addBlackboardArtifactType(String artifactTypeName, String displayName)3953 public BlackboardArtifact.Type addBlackboardArtifactType(String artifactTypeName, String displayName) throws TskCoreException, TskDataException { 3954 CaseDbConnection connection = connections.getConnection(); 3955 acquireSingleUserCaseWriteLock(); 3956 Statement s = null; 3957 ResultSet rs = null; 3958 try { 3959 connection.beginTransaction(); 3960 s = connection.createStatement(); 3961 rs = connection.executeQuery(s, "SELECT artifact_type_id FROM blackboard_artifact_types WHERE type_name = '" + artifactTypeName + "'"); //NON-NLS 3962 if (!rs.next()) { 3963 rs.close(); 3964 rs = connection.executeQuery(s, "SELECT MAX(artifact_type_id) AS highest_id FROM blackboard_artifact_types"); 3965 int maxID = 0; 3966 if (rs.next()) { 3967 maxID = rs.getInt("highest_id"); 3968 if (maxID < MIN_USER_DEFINED_TYPE_ID) { 3969 maxID = MIN_USER_DEFINED_TYPE_ID; 3970 } else { 3971 maxID++; 3972 } 3973 } 3974 connection.executeUpdate(s, "INSERT INTO blackboard_artifact_types (artifact_type_id, type_name, display_name) VALUES ('" + maxID + "', '" + artifactTypeName + "', '" + displayName + "')"); //NON-NLS 3975 BlackboardArtifact.Type type = new BlackboardArtifact.Type(maxID, artifactTypeName, displayName); 3976 this.typeIdToArtifactTypeMap.put(type.getTypeID(), type); 3977 this.typeNameToArtifactTypeMap.put(type.getTypeName(), type); 3978 connection.commitTransaction(); 3979 return type; 3980 } else { 3981 throw new TskDataException("The attribute type that was added was already within the system."); 3982 } 3983 } catch (SQLException ex) { 3984 connection.rollbackTransaction(); 3985 throw new TskCoreException("Error adding artifact type", ex); 3986 } finally { 3987 closeResultSet(rs); 3988 closeStatement(s); 3989 connection.close(); 3990 releaseSingleUserCaseWriteLock(); 3991 } 3992 } 3993 getBlackboardAttributes(final BlackboardArtifact artifact)3994 public ArrayList<BlackboardAttribute> getBlackboardAttributes(final BlackboardArtifact artifact) throws TskCoreException { 3995 CaseDbConnection connection = connections.getConnection(); 3996 acquireSingleUserCaseReadLock(); 3997 ResultSet rs = null; 3998 try { 3999 Statement statement = connection.createStatement(); 4000 rs = connection.executeQuery(statement, "SELECT attrs.artifact_id AS artifact_id, " 4001 + "attrs.source AS source, attrs.context AS context, attrs.attribute_type_id AS attribute_type_id, " 4002 + "attrs.value_type AS value_type, attrs.value_byte AS value_byte, " 4003 + "attrs.value_text AS value_text, attrs.value_int32 AS value_int32, " 4004 + "attrs.value_int64 AS value_int64, attrs.value_double AS value_double, " 4005 + "types.type_name AS type_name, types.display_name AS display_name " 4006 + "FROM blackboard_attributes AS attrs, blackboard_attribute_types AS types WHERE attrs.artifact_id = " + artifact.getArtifactID() 4007 + " AND attrs.attribute_type_id = types.attribute_type_id"); 4008 ArrayList<BlackboardAttribute> attributes = new ArrayList<BlackboardAttribute>(); 4009 while (rs.next()) { 4010 int attributeTypeId = rs.getInt("attribute_type_id"); 4011 String attributeTypeName = rs.getString("type_name"); 4012 BlackboardAttribute.Type attributeType; 4013 if (this.typeIdToAttributeTypeMap.containsKey(attributeTypeId)) { 4014 attributeType = this.typeIdToAttributeTypeMap.get(attributeTypeId); 4015 } else { 4016 attributeType = new BlackboardAttribute.Type(attributeTypeId, attributeTypeName, 4017 rs.getString("display_name"), 4018 BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getInt("value_type"))); 4019 this.typeIdToAttributeTypeMap.put(attributeTypeId, attributeType); 4020 this.typeNameToAttributeTypeMap.put(attributeTypeName, attributeType); 4021 } 4022 4023 final BlackboardAttribute attr = new BlackboardAttribute( 4024 rs.getLong("artifact_id"), 4025 attributeType, 4026 rs.getString("source"), 4027 rs.getString("context"), 4028 rs.getInt("value_int32"), 4029 rs.getLong("value_int64"), 4030 rs.getDouble("value_double"), 4031 rs.getString("value_text"), 4032 rs.getBytes("value_byte"), this 4033 ); 4034 attributes.add(attr); 4035 } 4036 return attributes; 4037 } catch (SQLException ex) { 4038 throw new TskCoreException("Error getting attributes for artifact, artifact id = " + artifact.getArtifactID(), ex); 4039 } finally { 4040 closeResultSet(rs); 4041 connection.close(); 4042 releaseSingleUserCaseReadLock(); 4043 } 4044 } 4045 4046 /** 4047 * Get all attributes that match a where clause. The clause should begin 4048 * with "WHERE" or "JOIN". To use this method you must know the database 4049 * tables 4050 * 4051 * @param whereClause a sqlite where clause 4052 * 4053 * @return a list of matching attributes 4054 * 4055 * @throws TskCoreException exception thrown if a critical error occurs 4056 * within tsk core \ref query_database_page 4057 */ getMatchingAttributes(String whereClause)4058 public ArrayList<BlackboardAttribute> getMatchingAttributes(String whereClause) throws TskCoreException { 4059 CaseDbConnection connection = connections.getConnection(); 4060 acquireSingleUserCaseReadLock(); 4061 Statement s = null; 4062 ResultSet rs = null; 4063 try { 4064 s = connection.createStatement(); 4065 rs = connection.executeQuery(s, "SELECT blackboard_attributes.artifact_id AS artifact_id, " 4066 + "blackboard_attributes.source AS source, blackboard_attributes.context AS context, " 4067 + "blackboard_attributes.attribute_type_id AS attribute_type_id, " 4068 + "blackboard_attributes.value_type AS value_type, blackboard_attributes.value_byte AS value_byte, " 4069 + "blackboard_attributes.value_text AS value_text, blackboard_attributes.value_int32 AS value_int32, " 4070 + "blackboard_attributes.value_int64 AS value_int64, blackboard_attributes.value_double AS value_double " 4071 + "FROM blackboard_attributes " + whereClause); //NON-NLS 4072 ArrayList<BlackboardAttribute> matches = new ArrayList<BlackboardAttribute>(); 4073 while (rs.next()) { 4074 BlackboardAttribute.Type type; 4075 // attribute type is cached, so this does not necessarily call to the db 4076 type = this.getAttributeType(rs.getInt("attribute_type_id")); 4077 BlackboardAttribute attr = new BlackboardAttribute( 4078 rs.getLong("artifact_id"), 4079 type, 4080 rs.getString("source"), 4081 rs.getString("context"), 4082 rs.getInt("value_int32"), 4083 rs.getLong("value_int64"), 4084 rs.getDouble("value_double"), 4085 rs.getString("value_text"), 4086 rs.getBytes("value_byte"), this 4087 ); 4088 matches.add(attr); 4089 } 4090 return matches; 4091 } catch (SQLException ex) { 4092 throw new TskCoreException("Error getting attributes using this where clause: " + whereClause, ex); 4093 } finally { 4094 closeResultSet(rs); 4095 closeStatement(s); 4096 connection.close(); 4097 releaseSingleUserCaseReadLock(); 4098 } 4099 } 4100 4101 /** 4102 * Get all artifacts that match a where clause. The clause should begin with 4103 * "WHERE" or "JOIN". To use this method you must know the database tables 4104 * 4105 * @param whereClause a sqlite where clause 4106 * 4107 * @return a list of matching artifacts 4108 * 4109 * @throws TskCoreException exception thrown if a critical error occurs 4110 * within tsk core \ref query_database_page 4111 */ getMatchingArtifacts(String whereClause)4112 public ArrayList<BlackboardArtifact> getMatchingArtifacts(String whereClause) throws TskCoreException { 4113 CaseDbConnection connection = connections.getConnection(); 4114 acquireSingleUserCaseReadLock(); 4115 ResultSet rs = null; 4116 Statement s = null; 4117 try { 4118 s = connection.createStatement(); 4119 rs = connection.executeQuery(s, "SELECT blackboard_artifacts.artifact_id AS artifact_id, " 4120 + "blackboard_artifacts.obj_id AS obj_id, blackboard_artifacts.artifact_obj_id AS artifact_obj_id, blackboard_artifacts.data_source_obj_id AS data_source_obj_id, blackboard_artifacts.artifact_type_id AS artifact_type_id, " 4121 + "blackboard_artifacts.review_status_id AS review_status_id " 4122 + "FROM blackboard_artifacts " + whereClause); //NON-NLS 4123 ArrayList<BlackboardArtifact> matches = new ArrayList<BlackboardArtifact>(); 4124 while (rs.next()) { 4125 BlackboardArtifact.Type type; 4126 // artifact type is cached, so this does not necessarily call to the db 4127 type = this.getArtifactType(rs.getInt("artifact_type_id")); 4128 BlackboardArtifact artifact = new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"), 4129 type.getTypeID(), type.getTypeName(), type.getDisplayName(), 4130 BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))); 4131 matches.add(artifact); 4132 } 4133 return matches; 4134 } catch (SQLException ex) { 4135 throw new TskCoreException("Error getting attributes using this where clause: " + whereClause, ex); 4136 } finally { 4137 closeResultSet(rs); 4138 closeStatement(s); 4139 connection.close(); 4140 releaseSingleUserCaseReadLock(); 4141 } 4142 } 4143 4144 /** 4145 * Add a new blackboard artifact with the given type. If that artifact type 4146 * does not exist an error will be thrown. The artifact type name can be 4147 * looked up in the returned blackboard artifact. 4148 * 4149 * @param artifactTypeID the type the given artifact should have 4150 * @param obj_id the content object id associated with this artifact 4151 * 4152 * @return a new blackboard artifact 4153 * 4154 * @throws TskCoreException exception thrown if a critical error occurs 4155 * within tsk core 4156 */ newBlackboardArtifact(int artifactTypeID, long obj_id)4157 public BlackboardArtifact newBlackboardArtifact(int artifactTypeID, long obj_id) throws TskCoreException { 4158 BlackboardArtifact.Type type = getArtifactType(artifactTypeID); 4159 return newBlackboardArtifact(artifactTypeID, obj_id, type.getTypeName(), type.getDisplayName()); 4160 } 4161 4162 /** 4163 * Add a new blackboard artifact with the given type. 4164 * 4165 * @param artifactType the type the given artifact should have 4166 * @param obj_id the content object id associated with this artifact 4167 * 4168 * @return a new blackboard artifact 4169 * 4170 * @throws TskCoreException exception thrown if a critical error occurs 4171 * within tsk core 4172 */ newBlackboardArtifact(ARTIFACT_TYPE artifactType, long obj_id)4173 public BlackboardArtifact newBlackboardArtifact(ARTIFACT_TYPE artifactType, long obj_id) throws TskCoreException { 4174 return newBlackboardArtifact(artifactType.getTypeID(), obj_id, artifactType.getLabel(), artifactType.getDisplayName()); 4175 } 4176 newBlackboardArtifact(int artifact_type_id, long obj_id, String artifactTypeName, String artifactDisplayName)4177 private BlackboardArtifact newBlackboardArtifact(int artifact_type_id, long obj_id, String artifactTypeName, String artifactDisplayName) throws TskCoreException { 4178 CaseDbConnection connection = connections.getConnection(); 4179 acquireSingleUserCaseWriteLock(); 4180 ResultSet resultSet = null; 4181 try { 4182 long artifact_obj_id = addObject(obj_id, TskData.ObjectType.ARTIFACT.getObjectType(), connection); 4183 long data_source_obj_id = getDataSourceObjectId(connection, obj_id); 4184 4185 PreparedStatement statement = null; 4186 if (dbType == DbType.POSTGRESQL) { 4187 statement = connection.getPreparedStatement(PREPARED_STATEMENT.POSTGRESQL_INSERT_ARTIFACT, Statement.RETURN_GENERATED_KEYS); 4188 statement.clearParameters(); 4189 statement.setLong(1, obj_id); 4190 statement.setLong(2, artifact_obj_id); 4191 statement.setLong(3, data_source_obj_id); 4192 statement.setInt(4, artifact_type_id); 4193 4194 } else { 4195 statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_ARTIFACT, Statement.RETURN_GENERATED_KEYS); 4196 statement.clearParameters(); 4197 this.nextArtifactId++; 4198 statement.setLong(1, this.nextArtifactId); 4199 statement.setLong(2, obj_id); 4200 statement.setLong(3, artifact_obj_id); 4201 statement.setLong(4, data_source_obj_id); 4202 statement.setInt(5, artifact_type_id); 4203 4204 } 4205 connection.executeUpdate(statement); 4206 resultSet = statement.getGeneratedKeys(); 4207 resultSet.next(); 4208 return new BlackboardArtifact(this, resultSet.getLong(1), //last_insert_rowid() 4209 obj_id, artifact_obj_id, data_source_obj_id, artifact_type_id, artifactTypeName, artifactDisplayName, BlackboardArtifact.ReviewStatus.UNDECIDED, true); 4210 } catch (SQLException ex) { 4211 throw new TskCoreException("Error creating a blackboard artifact", ex); 4212 } finally { 4213 closeResultSet(resultSet); 4214 connection.close(); 4215 releaseSingleUserCaseWriteLock(); 4216 } 4217 } 4218 4219 /** 4220 * Checks if the content object has children. Note: this is generally more 4221 * efficient then preloading all children and checking if the set is empty, 4222 * and facilities lazy loading. 4223 * 4224 * @param content content object to check for children 4225 * 4226 * @return true if has children, false otherwise 4227 * 4228 * @throws TskCoreException exception thrown if a critical error occurs 4229 * within tsk core 4230 */ getContentHasChildren(Content content)4231 boolean getContentHasChildren(Content content) throws TskCoreException { 4232 CaseDbConnection connection = connections.getConnection(); 4233 acquireSingleUserCaseReadLock(); 4234 ResultSet rs = null; 4235 try { 4236 // SELECT COUNT(obj_id) AS count FROM tsk_objects WHERE par_obj_id = ? 4237 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_CHILD_OBJECTS_BY_PARENT); 4238 statement.clearParameters(); 4239 statement.setLong(1, content.getId()); 4240 rs = connection.executeQuery(statement); 4241 boolean hasChildren = false; 4242 if (rs.next()) { 4243 hasChildren = rs.getInt("count") > 0; 4244 } 4245 return hasChildren; 4246 } catch (SQLException e) { 4247 throw new TskCoreException("Error checking for children of parent " + content, e); 4248 } finally { 4249 closeResultSet(rs); 4250 connection.close(); 4251 releaseSingleUserCaseReadLock(); 4252 } 4253 } 4254 4255 /** 4256 * Counts if the content object children. Note: this is generally more 4257 * efficient then preloading all children and counting, and facilities lazy 4258 * loading. 4259 * 4260 * @param content content object to check for children count 4261 * 4262 * @return children count 4263 * 4264 * @throws TskCoreException exception thrown if a critical error occurs 4265 * within tsk core 4266 */ getContentChildrenCount(Content content)4267 int getContentChildrenCount(Content content) throws TskCoreException { 4268 4269 if (!this.getHasChildren(content)) { 4270 return 0; 4271 } 4272 4273 CaseDbConnection connection = connections.getConnection(); 4274 acquireSingleUserCaseReadLock(); 4275 ResultSet rs = null; 4276 try { 4277 // SELECT COUNT(obj_id) AS count FROM tsk_objects WHERE par_obj_id = ? 4278 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_CHILD_OBJECTS_BY_PARENT); 4279 statement.clearParameters(); 4280 statement.setLong(1, content.getId()); 4281 rs = connection.executeQuery(statement); 4282 int countChildren = -1; 4283 if (rs.next()) { 4284 countChildren = rs.getInt("count"); 4285 } 4286 return countChildren; 4287 } catch (SQLException e) { 4288 throw new TskCoreException("Error checking for children of parent " + content, e); 4289 } finally { 4290 closeResultSet(rs); 4291 connection.close(); 4292 releaseSingleUserCaseReadLock(); 4293 } 4294 } 4295 4296 /** 4297 * Returns the list of AbstractFile Children of a given type for a given 4298 * AbstractFileParent 4299 * 4300 * @param parent the content parent to get abstract file children for 4301 * @param type children type to look for, defined in 4302 * TSK_DB_FILES_TYPE_ENUM 4303 * 4304 * @throws TskCoreException exception thrown if a critical error occurs 4305 * within tsk core 4306 */ getAbstractFileChildren(Content parent, TSK_DB_FILES_TYPE_ENUM type)4307 List<Content> getAbstractFileChildren(Content parent, TSK_DB_FILES_TYPE_ENUM type) throws TskCoreException { 4308 CaseDbConnection connection = connections.getConnection(); 4309 acquireSingleUserCaseReadLock(); 4310 ResultSet rs = null; 4311 try { 4312 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_PARENT_AND_TYPE); 4313 statement.clearParameters(); 4314 long parentId = parent.getId(); 4315 statement.setLong(1, parentId); 4316 statement.setShort(2, type.getFileType()); 4317 rs = connection.executeQuery(statement); 4318 return fileChildren(rs, connection, parentId); 4319 } catch (SQLException ex) { 4320 throw new TskCoreException("Error getting AbstractFile children for Content", ex); 4321 } finally { 4322 closeResultSet(rs); 4323 connection.close(); 4324 releaseSingleUserCaseReadLock(); 4325 } 4326 } 4327 4328 /** 4329 * Returns the list of all AbstractFile Children for a given 4330 * AbstractFileParent 4331 * 4332 * @param parent the content parent to get abstract file children for 4333 * 4334 * @throws TskCoreException exception thrown if a critical error occurs 4335 * within tsk core 4336 */ getAbstractFileChildren(Content parent)4337 List<Content> getAbstractFileChildren(Content parent) throws TskCoreException { 4338 CaseDbConnection connection = connections.getConnection(); 4339 acquireSingleUserCaseReadLock(); 4340 ResultSet rs = null; 4341 try { 4342 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_PARENT); 4343 statement.clearParameters(); 4344 long parentId = parent.getId(); 4345 statement.setLong(1, parentId); 4346 rs = connection.executeQuery(statement); 4347 return fileChildren(rs, connection, parentId); 4348 } catch (SQLException ex) { 4349 throw new TskCoreException("Error getting AbstractFile children for Content", ex); 4350 } finally { 4351 closeResultSet(rs); 4352 connection.close(); 4353 releaseSingleUserCaseReadLock(); 4354 } 4355 } 4356 4357 /** 4358 * Get list of IDs for abstract files of a given type that are children of a 4359 * given content. 4360 * 4361 * @param parent Object to find children for 4362 * @param type Type of children to find IDs for 4363 * 4364 * @return 4365 * 4366 * @throws TskCoreException 4367 */ getAbstractFileChildrenIds(Content parent, TSK_DB_FILES_TYPE_ENUM type)4368 List<Long> getAbstractFileChildrenIds(Content parent, TSK_DB_FILES_TYPE_ENUM type) throws TskCoreException { 4369 CaseDbConnection connection = connections.getConnection(); 4370 acquireSingleUserCaseReadLock(); 4371 ResultSet rs = null; 4372 try { 4373 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_IDS_BY_PARENT_AND_TYPE); 4374 statement.clearParameters(); 4375 statement.setLong(1, parent.getId()); 4376 statement.setShort(2, type.getFileType()); 4377 rs = connection.executeQuery(statement); 4378 List<Long> children = new ArrayList<Long>(); 4379 while (rs.next()) { 4380 children.add(rs.getLong("obj_id")); 4381 } 4382 return children; 4383 } catch (SQLException ex) { 4384 throw new TskCoreException("Error getting AbstractFile children for Content", ex); 4385 } finally { 4386 closeResultSet(rs); 4387 connection.close(); 4388 releaseSingleUserCaseReadLock(); 4389 } 4390 } 4391 4392 /** 4393 * Get list of IDs for abstract files that are children of a given content. 4394 * 4395 * @param parent Object to find children for 4396 * 4397 * @return 4398 * 4399 * @throws TskCoreException 4400 */ getAbstractFileChildrenIds(Content parent)4401 List<Long> getAbstractFileChildrenIds(Content parent) throws TskCoreException { 4402 CaseDbConnection connection = connections.getConnection(); 4403 acquireSingleUserCaseReadLock(); 4404 ResultSet rs = null; 4405 try { 4406 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_IDS_BY_PARENT); 4407 statement.clearParameters(); 4408 statement.setLong(1, parent.getId()); 4409 rs = connection.executeQuery(statement); 4410 List<Long> children = new ArrayList<Long>(); 4411 while (rs.next()) { 4412 children.add(rs.getLong("obj_id")); 4413 } 4414 return children; 4415 } catch (SQLException ex) { 4416 throw new TskCoreException("Error getting AbstractFile children for Content", ex); 4417 } finally { 4418 closeResultSet(rs); 4419 connection.close(); 4420 releaseSingleUserCaseReadLock(); 4421 } 4422 } 4423 4424 /** 4425 * Get list of object IDs for artifacts that are children of a given 4426 * content. 4427 * 4428 * @param parent Object to find children for 4429 * 4430 * @return 4431 * 4432 * @throws TskCoreException 4433 */ getBlackboardArtifactChildrenIds(Content parent)4434 List<Long> getBlackboardArtifactChildrenIds(Content parent) throws TskCoreException { 4435 CaseDbConnection connection = connections.getConnection(); 4436 acquireSingleUserCaseReadLock(); 4437 ResultSet rs = null; 4438 try { 4439 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ARTIFACT_OBJECTIDS_BY_PARENT); 4440 statement.clearParameters(); 4441 statement.setLong(1, parent.getId()); 4442 rs = connection.executeQuery(statement); 4443 List<Long> children = new ArrayList<Long>(); 4444 while (rs.next()) { 4445 children.add(rs.getLong("obj_id")); 4446 } 4447 return children; 4448 } catch (SQLException ex) { 4449 throw new TskCoreException("Error getting children for BlackboardArtifact", ex); 4450 } finally { 4451 closeResultSet(rs); 4452 connection.close(); 4453 releaseSingleUserCaseReadLock(); 4454 } 4455 } 4456 4457 /** 4458 * Get list of artifacts that are children of a given content. 4459 * 4460 * @param parent Object to find children for 4461 * 4462 * @return 4463 * 4464 * @throws TskCoreException 4465 */ getBlackboardArtifactChildren(Content parent)4466 List<Content> getBlackboardArtifactChildren(Content parent) throws TskCoreException { 4467 4468 long parentId = parent.getId(); 4469 ArrayList<BlackboardArtifact> artsArray = getArtifactsHelper("blackboard_artifacts.obj_id = " + parentId + ";"); 4470 4471 List<Content> lc = new ArrayList<Content>(); 4472 lc.addAll(artsArray); 4473 return lc; 4474 } 4475 4476 /** 4477 * Get info about children of a given Content from the database. 4478 * 4479 * @param c Parent object to run query against 4480 * 4481 * @throws TskCoreException exception thrown if a critical error occurs 4482 * within tsk core 4483 */ getChildrenInfo(Content c)4484 Collection<ObjectInfo> getChildrenInfo(Content c) throws TskCoreException { 4485 CaseDbConnection connection = connections.getConnection(); 4486 acquireSingleUserCaseReadLock(); 4487 Statement s = null; 4488 ResultSet rs = null; 4489 try { 4490 s = connection.createStatement(); 4491 rs = connection.executeQuery(s, "SELECT tsk_objects.obj_id AS obj_id, tsk_objects.type AS type " //NON-NLS 4492 + "FROM tsk_objects LEFT JOIN tsk_files " //NON-NLS 4493 + "ON tsk_objects.obj_id = tsk_files.obj_id " //NON-NLS 4494 + "WHERE tsk_objects.par_obj_id = " + c.getId() 4495 + " ORDER BY tsk_objects.obj_id"); //NON-NLS 4496 Collection<ObjectInfo> infos = new ArrayList<ObjectInfo>(); 4497 while (rs.next()) { 4498 infos.add(new ObjectInfo(rs.getLong("obj_id"), ObjectType.valueOf(rs.getShort("type")))); //NON-NLS 4499 } 4500 return infos; 4501 } catch (SQLException ex) { 4502 throw new TskCoreException("Error getting Children Info for Content", ex); 4503 } finally { 4504 closeResultSet(rs); 4505 closeStatement(s); 4506 connection.close(); 4507 releaseSingleUserCaseReadLock(); 4508 } 4509 } 4510 4511 /** 4512 * Get parent info for the parent of the content object 4513 * 4514 * @param c content object to get parent info for 4515 * 4516 * @return the parent object info with the parent object type and id 4517 * 4518 * @throws TskCoreException exception thrown if a critical error occurs 4519 * within tsk core 4520 */ getParentInfo(Content c)4521 ObjectInfo getParentInfo(Content c) throws TskCoreException { 4522 return getParentInfo(c.getId()); 4523 } 4524 4525 /** 4526 * Get parent info for the parent of the content object id 4527 * 4528 * @param id content object id to get parent info for 4529 * 4530 * @return the parent object info with the parent object type and id 4531 * 4532 * @throws TskCoreException exception thrown if a critical error occurs 4533 * within tsk core 4534 */ getParentInfo(long contentId)4535 ObjectInfo getParentInfo(long contentId) throws TskCoreException { 4536 CaseDbConnection connection = connections.getConnection(); 4537 acquireSingleUserCaseReadLock(); 4538 Statement s = null; 4539 ResultSet rs = null; 4540 try { 4541 s = connection.createStatement(); 4542 rs = connection.executeQuery(s, "SELECT parent.obj_id AS obj_id, parent.type AS type " //NON-NLS 4543 + "FROM tsk_objects AS parent INNER JOIN tsk_objects AS child " //NON-NLS 4544 + "ON child.par_obj_id = parent.obj_id " //NON-NLS 4545 + "WHERE child.obj_id = " + contentId); //NON-NLS 4546 if (rs.next()) { 4547 return new ObjectInfo(rs.getLong("obj_id"), ObjectType.valueOf(rs.getShort("type"))); 4548 } else { 4549 return null; 4550 } 4551 } catch (SQLException ex) { 4552 throw new TskCoreException("Error getting Parent Info for Content: " + contentId, ex); 4553 } finally { 4554 closeResultSet(rs); 4555 closeStatement(s); 4556 connection.close(); 4557 releaseSingleUserCaseReadLock(); 4558 } 4559 } 4560 4561 /** 4562 * Gets parent directory for FsContent object 4563 * 4564 * @param fsc FsContent to get parent dir for 4565 * 4566 * @return the parent Directory or null if the Content has no parent 4567 * 4568 * @throws TskCoreException thrown if critical error occurred within tsk 4569 * core 4570 */ getParentDirectory(FsContent fsc)4571 Directory getParentDirectory(FsContent fsc) throws TskCoreException { 4572 if (fsc.isRoot()) { 4573 // Given FsContent is a root object and can't have parent directory 4574 return null; 4575 } else { 4576 ObjectInfo parentInfo = getParentInfo(fsc); 4577 if (parentInfo == null) { 4578 return null; 4579 } 4580 Directory parent = null; 4581 if (parentInfo.type == ObjectType.ABSTRACTFILE) { 4582 parent = getDirectoryById(parentInfo.id, fsc.getFileSystem()); 4583 } else { 4584 throw new TskCoreException("Parent of FsContent (id: " + fsc.getId() + ") has wrong type to be directory: " + parentInfo.type); 4585 } 4586 return parent; 4587 } 4588 } 4589 4590 /** 4591 * Get content object by content id 4592 * 4593 * @param id to get content object for 4594 * 4595 * @return instance of a Content object (one of its subclasses), or null if 4596 * not found. 4597 * 4598 * @throws TskCoreException thrown if critical error occurred within tsk 4599 * core 4600 */ getContentById(long id)4601 public Content getContentById(long id) throws TskCoreException { 4602 // First check to see if this exists in our frequently used content cache. 4603 Content content = frequentlyUsedContentMap.get(id); 4604 if (null != content) { 4605 return content; 4606 } 4607 4608 CaseDbConnection connection = connections.getConnection(); 4609 acquireSingleUserCaseReadLock(); 4610 Statement s = null; 4611 ResultSet rs = null; 4612 long parentId; 4613 TskData.ObjectType type; 4614 4615 try { 4616 s = connection.createStatement(); 4617 rs = connection.executeQuery(s, "SELECT * FROM tsk_objects WHERE obj_id = " + id + " LIMIT 1"); //NON-NLS 4618 if (!rs.next()) { 4619 return null; 4620 } 4621 parentId = rs.getLong("par_obj_id"); //NON-NLS 4622 type = TskData.ObjectType.valueOf(rs.getShort("type")); //NON-NLS 4623 } catch (SQLException ex) { 4624 throw new TskCoreException("Error getting Content by ID.", ex); 4625 } finally { 4626 closeResultSet(rs); 4627 closeStatement(s); 4628 connection.close(); 4629 releaseSingleUserCaseReadLock(); 4630 } 4631 4632 // Construct the object 4633 switch (type) { 4634 case IMG: 4635 content = getImageById(id); 4636 frequentlyUsedContentMap.put(id, content); 4637 break; 4638 case VS: 4639 content = getVolumeSystemById(id, parentId); 4640 break; 4641 case VOL: 4642 content = getVolumeById(id, parentId); 4643 frequentlyUsedContentMap.put(id, content); 4644 break; 4645 case FS: 4646 content = getFileSystemById(id, parentId); 4647 frequentlyUsedContentMap.put(id, content); 4648 break; 4649 case ABSTRACTFILE: 4650 content = getAbstractFileById(id); 4651 4652 // Add virtual and root directories to frequently used map. 4653 // Calling isRoot() on local directories goes up the entire directory structure 4654 // and they can only be the root of portable cases, so skip trying to add 4655 // them to the cache. 4656 if (((AbstractFile) content).isVirtual() 4657 || ((!(content instanceof LocalDirectory)) && ((AbstractFile) content).isRoot())) { 4658 frequentlyUsedContentMap.put(id, content); 4659 } 4660 break; 4661 case ARTIFACT: 4662 content = getArtifactById(id); 4663 break; 4664 case REPORT: 4665 content = getReportById(id); 4666 break; 4667 default: 4668 throw new TskCoreException("Could not obtain Content object with ID: " + id); 4669 } 4670 4671 return content; 4672 } 4673 4674 /** 4675 * Get a path of a file in tsk_files_path table or null if there is none 4676 * 4677 * @param id id of the file to get path for 4678 * 4679 * @return file path or null 4680 */ getFilePath(long id)4681 String getFilePath(long id) { 4682 CaseDbConnection connection; 4683 try { 4684 connection = connections.getConnection(); 4685 } catch (TskCoreException ex) { 4686 logger.log(Level.SEVERE, "Error getting file path for file " + id, ex); //NON-NLS 4687 return null; 4688 } 4689 String filePath = null; 4690 acquireSingleUserCaseReadLock(); 4691 ResultSet rs = null; 4692 try { 4693 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_LOCAL_PATH_FOR_FILE); 4694 statement.clearParameters(); 4695 statement.setLong(1, id); 4696 rs = connection.executeQuery(statement); 4697 if (rs.next()) { 4698 filePath = rs.getString("path"); 4699 } 4700 } catch (SQLException ex) { 4701 logger.log(Level.SEVERE, "Error getting file path for file " + id, ex); //NON-NLS 4702 } finally { 4703 closeResultSet(rs); 4704 connection.close(); 4705 releaseSingleUserCaseReadLock(); 4706 } 4707 return filePath; 4708 } 4709 4710 /** 4711 * Get the encoding type for a file in tsk_files_path table 4712 * 4713 * @param id id of the file to get path for 4714 * 4715 * @return Encoding type (NONE if nothing was found) 4716 */ getEncodingType(long id)4717 TskData.EncodingType getEncodingType(long id) { 4718 CaseDbConnection connection; 4719 try { 4720 connection = connections.getConnection(); 4721 } catch (TskCoreException ex) { 4722 logger.log(Level.SEVERE, "Error getting file path for file " + id, ex); //NON-NLS 4723 return null; 4724 } 4725 TskData.EncodingType type = TskData.EncodingType.NONE; 4726 acquireSingleUserCaseReadLock(); 4727 ResultSet rs = null; 4728 try { 4729 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ENCODING_FOR_FILE); 4730 statement.clearParameters(); 4731 statement.setLong(1, id); 4732 rs = connection.executeQuery(statement); 4733 if (rs.next()) { 4734 type = TskData.EncodingType.valueOf(rs.getInt(1)); 4735 } 4736 } catch (SQLException ex) { 4737 logger.log(Level.SEVERE, "Error getting encoding type for file " + id, ex); //NON-NLS 4738 } finally { 4739 closeResultSet(rs); 4740 connection.close(); 4741 releaseSingleUserCaseReadLock(); 4742 } 4743 return type; 4744 } 4745 4746 /** 4747 * Gets the parent_path of a file. 4748 * 4749 * @param objectId The object id of the file. 4750 * @param connection An open database connection. 4751 * 4752 * @return The path of the file or null. 4753 */ getFileParentPath(long objectId, CaseDbConnection connection)4754 String getFileParentPath(long objectId, CaseDbConnection connection) { 4755 String parentPath = null; 4756 acquireSingleUserCaseReadLock(); 4757 ResultSet rs = null; 4758 try { 4759 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_PATH_FOR_FILE); 4760 statement.clearParameters(); 4761 statement.setLong(1, objectId); 4762 rs = connection.executeQuery(statement); 4763 if (rs.next()) { 4764 parentPath = rs.getString("parent_path"); 4765 } 4766 } catch (SQLException ex) { 4767 logger.log(Level.SEVERE, "Error getting file parent_path for file " + objectId, ex); //NON-NLS 4768 } finally { 4769 closeResultSet(rs); 4770 releaseSingleUserCaseReadLock(); 4771 } 4772 return parentPath; 4773 } 4774 4775 /** 4776 * Gets the name of a file. 4777 * 4778 * @param objectId The object id of the file. 4779 * @param connection An open database connection. 4780 * 4781 * @return The path of the file or null. 4782 */ getFileName(long objectId, CaseDbConnection connection)4783 String getFileName(long objectId, CaseDbConnection connection) { 4784 String fileName = null; 4785 acquireSingleUserCaseReadLock(); 4786 ResultSet rs = null; 4787 try { 4788 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_NAME); 4789 statement.clearParameters(); 4790 statement.setLong(1, objectId); 4791 rs = connection.executeQuery(statement); 4792 if (rs.next()) { 4793 fileName = rs.getString("name"); 4794 } 4795 } catch (SQLException ex) { 4796 logger.log(Level.SEVERE, "Error getting file parent_path for file " + objectId, ex); //NON-NLS 4797 } finally { 4798 closeResultSet(rs); 4799 releaseSingleUserCaseReadLock(); 4800 } 4801 return fileName; 4802 } 4803 4804 /** 4805 * Get a derived method for a file, or null if none 4806 * 4807 * @param id id of the derived file 4808 * 4809 * @return derived method or null if not present 4810 * 4811 * @throws TskCoreException exception throws if core error occurred and 4812 * method could not be queried 4813 */ getDerivedMethod(long id)4814 DerivedFile.DerivedMethod getDerivedMethod(long id) throws TskCoreException { 4815 CaseDbConnection connection = connections.getConnection(); 4816 DerivedFile.DerivedMethod method = null; 4817 acquireSingleUserCaseReadLock(); 4818 ResultSet rs1 = null; 4819 ResultSet rs2 = null; 4820 try { 4821 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_DERIVED_FILE); 4822 statement.clearParameters(); 4823 statement.setLong(1, id); 4824 rs1 = connection.executeQuery(statement); 4825 if (rs1.next()) { 4826 int method_id = rs1.getInt("derived_id"); 4827 String rederive = rs1.getString("rederive"); 4828 method = new DerivedFile.DerivedMethod(method_id, rederive); 4829 statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_DERIVATION_METHOD); 4830 statement.clearParameters(); 4831 statement.setInt(1, method_id); 4832 rs2 = connection.executeQuery(statement); 4833 if (rs2.next()) { 4834 method.setToolName(rs2.getString("tool_name")); 4835 method.setToolVersion(rs2.getString("tool_version")); 4836 method.setOther(rs2.getString("other")); 4837 } 4838 } 4839 } catch (SQLException e) { 4840 logger.log(Level.SEVERE, "Error getting derived method for file: " + id, e); //NON-NLS 4841 } finally { 4842 closeResultSet(rs2); 4843 closeResultSet(rs1); 4844 connection.close(); 4845 releaseSingleUserCaseReadLock(); 4846 } 4847 return method; 4848 } 4849 4850 /** 4851 * Get abstract file object from tsk_files table by its id 4852 * 4853 * @param id id of the file object in tsk_files table 4854 * 4855 * @return AbstractFile object populated, or null if not found. 4856 * 4857 * @throws TskCoreException thrown if critical error occurred within tsk 4858 * core and file could not be queried 4859 */ getAbstractFileById(long id)4860 public AbstractFile getAbstractFileById(long id) throws TskCoreException { 4861 CaseDbConnection connection = connections.getConnection(); 4862 try { 4863 return getAbstractFileById(id, connection); 4864 } finally { 4865 connection.close(); 4866 } 4867 } 4868 4869 /** 4870 * Get abstract file object from tsk_files table by its id on an existing 4871 * connection. 4872 * 4873 * @param objectId The id of the file object in tsk_files table. 4874 * @param connection An open database connection. 4875 * 4876 * @return AbstractFile object populated, or null if not found. 4877 * 4878 * @throws TskCoreException thrown if critical error occurred within tsk 4879 * core and file could not be queried 4880 */ getAbstractFileById(long objectId, CaseDbConnection connection)4881 AbstractFile getAbstractFileById(long objectId, CaseDbConnection connection) throws TskCoreException { 4882 acquireSingleUserCaseReadLock(); 4883 ResultSet rs = null; 4884 try { 4885 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_BY_ID); 4886 statement.clearParameters(); 4887 statement.setLong(1, objectId); 4888 rs = connection.executeQuery(statement); 4889 List<AbstractFile> files = resultSetToAbstractFiles(rs, connection); 4890 if (files.size() > 0) { 4891 return files.get(0); 4892 } else { 4893 return null; 4894 } 4895 } catch (SQLException ex) { 4896 throw new TskCoreException("Error getting file by id, id = " + objectId, ex); 4897 } finally { 4898 closeResultSet(rs); 4899 releaseSingleUserCaseReadLock(); 4900 } 4901 } 4902 4903 /** 4904 * Get artifact from blackboard_artifacts table by its artifact_obj_id 4905 * 4906 * @param id id of the artifact in blackboard_artifacts table 4907 * 4908 * @return Artifact object populated, or null if not found. 4909 * 4910 * @throws TskCoreException thrown if critical error occurred within tsk 4911 * core and file could not be queried 4912 */ getArtifactById(long id)4913 public BlackboardArtifact getArtifactById(long id) throws TskCoreException { 4914 CaseDbConnection connection = connections.getConnection(); 4915 acquireSingleUserCaseReadLock(); 4916 ResultSet rs = null; 4917 try { 4918 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ARTIFACT_BY_ARTIFACT_OBJ_ID); 4919 statement.clearParameters(); 4920 statement.setLong(1, id); 4921 rs = connection.executeQuery(statement); 4922 List<BlackboardArtifact> artifacts = resultSetToArtifacts(rs); 4923 if (artifacts.size() > 0) { 4924 return artifacts.get(0); 4925 } else { 4926 return null; 4927 } 4928 } catch (SQLException ex) { 4929 throw new TskCoreException("Error getting artifacts by artifact_obj_id, artifact_obj_id = " + id, ex); 4930 } finally { 4931 closeResultSet(rs); 4932 connection.close(); 4933 releaseSingleUserCaseReadLock(); 4934 } 4935 } 4936 4937 /** 4938 * Get artifact from blackboard_artifacts table by its artifact_id 4939 * 4940 * @param id Artifact ID of the artifact in blackboard_artifacts table 4941 * 4942 * @return Artifact object populated, or null if not found. 4943 * 4944 * @throws TskCoreException thrown if critical error occurred within tsk 4945 * core and file could not be queried 4946 */ getArtifactByArtifactId(long id)4947 public BlackboardArtifact getArtifactByArtifactId(long id) throws TskCoreException { 4948 CaseDbConnection connection = connections.getConnection(); 4949 acquireSingleUserCaseReadLock(); 4950 ResultSet rs = null; 4951 try { 4952 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ARTIFACT_BY_ARTIFACT_ID); 4953 statement.clearParameters(); 4954 statement.setLong(1, id); 4955 rs = connection.executeQuery(statement); 4956 List<BlackboardArtifact> artifacts = resultSetToArtifacts(rs); 4957 if (artifacts.size() > 0) { 4958 return artifacts.get(0); 4959 } else { 4960 return null; 4961 } 4962 } catch (SQLException ex) { 4963 throw new TskCoreException("Error getting artifacts by artifact id, artifact id = " + id, ex); 4964 } finally { 4965 closeResultSet(rs); 4966 connection.close(); 4967 releaseSingleUserCaseReadLock(); 4968 } 4969 } 4970 4971 /** 4972 * Get the object ID of the file system that a file is located in. 4973 * 4974 * Note: for FsContent files, this is the real fs for other non-fs 4975 * AbstractFile files, this field is used internally for data source id (the 4976 * root content obj) 4977 * 4978 * @param fileId object id of the file to get fs column id for 4979 * @param connection the database connection to use 4980 * 4981 * @return fs_id or -1 if not present 4982 */ getFileSystemId(long fileId, CaseDbConnection connection)4983 private long getFileSystemId(long fileId, CaseDbConnection connection) { 4984 acquireSingleUserCaseReadLock(); 4985 ResultSet rs = null; 4986 long ret = -1; 4987 try { 4988 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_SYSTEM_BY_OBJECT); 4989 statement.clearParameters(); 4990 statement.setLong(1, fileId); 4991 rs = connection.executeQuery(statement); 4992 if (rs.next()) { 4993 ret = rs.getLong("fs_obj_id"); 4994 if (ret == 0) { 4995 ret = -1; 4996 } 4997 } 4998 } catch (SQLException e) { 4999 logger.log(Level.SEVERE, "Error checking file system id of a file, id = " + fileId, e); //NON-NLS 5000 } finally { 5001 closeResultSet(rs); 5002 releaseSingleUserCaseReadLock(); 5003 } 5004 return ret; 5005 } 5006 5007 /** 5008 * Checks if the file is a (sub)child of the data source (parentless Content 5009 * object such as Image or VirtualDirectory representing filesets) 5010 * 5011 * @param dataSource dataSource to check 5012 * @param fileId id of file to check 5013 * 5014 * @return true if the file is in the dataSource hierarchy 5015 * 5016 * @throws TskCoreException thrown if check failed 5017 */ isFileFromSource(Content dataSource, long fileId)5018 public boolean isFileFromSource(Content dataSource, long fileId) throws TskCoreException { 5019 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 5020 CaseDbConnection connection = connections.getConnection(); 5021 acquireSingleUserCaseReadLock(); 5022 Statement statement = null; 5023 ResultSet resultSet = null; 5024 try { 5025 statement = connection.createStatement(); 5026 resultSet = connection.executeQuery(statement, query); 5027 resultSet.next(); 5028 return (resultSet.getLong("count") > 0L); 5029 } catch (SQLException ex) { 5030 throw new TskCoreException(String.format("Error executing query %s", query), ex); 5031 } finally { 5032 closeResultSet(resultSet); 5033 closeStatement(statement); 5034 connection.close(); 5035 releaseSingleUserCaseReadLock(); 5036 } 5037 } 5038 5039 /** 5040 * @param dataSource the dataSource (Image, parent-less VirtualDirectory) to 5041 * search for the given file name 5042 * @param fileName Pattern of the name of the file or directory to match 5043 * (case insensitive, used in LIKE SQL statement). 5044 * 5045 * @return a list of AbstractFile for files/directories whose name matches 5046 * the given fileName 5047 * 5048 * @throws TskCoreException thrown if check failed 5049 */ findFiles(Content dataSource, String fileName)5050 public List<AbstractFile> findFiles(Content dataSource, String fileName) throws TskCoreException { 5051 List<AbstractFile> files = new ArrayList<AbstractFile>(); 5052 CaseDbConnection connection = connections.getConnection(); 5053 acquireSingleUserCaseReadLock(); 5054 ResultSet resultSet = null; 5055 try { 5056 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_DATA_SOURCE_AND_NAME); 5057 statement.clearParameters(); 5058 statement.setString(1, fileName.toLowerCase()); 5059 statement.setLong(2, dataSource.getId()); 5060 resultSet = connection.executeQuery(statement); 5061 files.addAll(resultSetToAbstractFiles(resultSet, connection)); 5062 } catch (SQLException e) { 5063 throw new TskCoreException(bundle.getString("SleuthkitCase.findFiles.exception.msg3.text"), e); 5064 } finally { 5065 closeResultSet(resultSet); 5066 connection.close(); 5067 releaseSingleUserCaseReadLock(); 5068 } 5069 return files; 5070 } 5071 5072 /** 5073 * @param dataSource the dataSource (Image, parent-less VirtualDirectory) 5074 * to search for the given file name 5075 * @param fileName Pattern of the name of the file or directory to match 5076 * (case insensitive, used in LIKE SQL statement). 5077 * @param dirSubString Substring that must exist in parent path. Will be 5078 * surrounded by % in LIKE query 5079 * 5080 * @return a list of AbstractFile for files/directories whose name matches 5081 * fileName and whose parent directory contains dirName. 5082 * 5083 * @throws org.sleuthkit.datamodel.TskCoreException 5084 */ findFiles(Content dataSource, String fileName, String dirSubString)5085 public List<AbstractFile> findFiles(Content dataSource, String fileName, String dirSubString) throws TskCoreException { 5086 List<AbstractFile> files = new ArrayList<AbstractFile>(); 5087 CaseDbConnection connection = connections.getConnection(); 5088 acquireSingleUserCaseReadLock(); 5089 ResultSet resultSet = null; 5090 try { 5091 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_DATA_SOURCE_AND_PARENT_PATH_AND_NAME); 5092 statement.clearParameters(); 5093 statement.setString(1, fileName.toLowerCase()); 5094 statement.setString(2, "%" + dirSubString.toLowerCase() + "%"); //NON-NLS 5095 statement.setLong(3, dataSource.getId()); 5096 resultSet = connection.executeQuery(statement); 5097 files.addAll(resultSetToAbstractFiles(resultSet, connection)); 5098 } catch (SQLException e) { 5099 throw new TskCoreException(bundle.getString("SleuthkitCase.findFiles3.exception.msg3.text"), e); 5100 } finally { 5101 closeResultSet(resultSet); 5102 connection.close(); 5103 releaseSingleUserCaseReadLock(); 5104 } 5105 return files; 5106 } 5107 5108 /** 5109 * Adds a virtual directory to the database and returns a VirtualDirectory 5110 * object representing it. 5111 * 5112 * @param parentId the ID of the parent, or 0 if NULL 5113 * @param directoryName the name of the virtual directory to create 5114 * 5115 * @return 5116 * 5117 * @throws TskCoreException 5118 */ addVirtualDirectory(long parentId, String directoryName)5119 public VirtualDirectory addVirtualDirectory(long parentId, String directoryName) throws TskCoreException { 5120 CaseDbTransaction localTrans = beginTransaction(); 5121 localTrans.acquireSingleUserCaseWriteLock(); 5122 try { 5123 VirtualDirectory newVD = addVirtualDirectory(parentId, directoryName, localTrans); 5124 localTrans.commit(); 5125 localTrans = null; 5126 return newVD; 5127 } finally { 5128 // NOTE: write lock will be released by transaction 5129 if (null != localTrans) { 5130 try { 5131 localTrans.rollback(); 5132 } catch (TskCoreException ex2) { 5133 logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex2); 5134 } 5135 } 5136 } 5137 } 5138 5139 /** 5140 * Add an object to the tsk_objects table. Returns the object ID for the new 5141 * object. 5142 * 5143 * @param parentId Parent of the new object 5144 * @param objectType Type of the new object 5145 * @param connection Case connection 5146 * 5147 * @return the object ID for the new object 5148 * 5149 * @throws SQLException 5150 */ addObject(long parentId, int objectType, CaseDbConnection connection)5151 private long addObject(long parentId, int objectType, CaseDbConnection connection) throws SQLException { 5152 ResultSet resultSet = null; 5153 acquireSingleUserCaseWriteLock(); 5154 try { 5155 // INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?) 5156 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_OBJECT, Statement.RETURN_GENERATED_KEYS); 5157 statement.clearParameters(); 5158 if (parentId != 0) { 5159 statement.setLong(1, parentId); 5160 } else { 5161 statement.setNull(1, java.sql.Types.BIGINT); 5162 } 5163 statement.setInt(2, objectType); 5164 connection.executeUpdate(statement); 5165 resultSet = statement.getGeneratedKeys(); 5166 5167 if (resultSet.next()) { 5168 if (parentId != 0) { 5169 setHasChildren(parentId); 5170 } 5171 return resultSet.getLong(1); //last_insert_rowid() 5172 } else { 5173 throw new SQLException("Error inserting object with parent " + parentId + " into tsk_objects"); 5174 } 5175 } finally { 5176 closeResultSet(resultSet); 5177 releaseSingleUserCaseWriteLock(); 5178 } 5179 } 5180 5181 /** 5182 * Adds a virtual directory to the database and returns a VirtualDirectory 5183 * object representing it. 5184 * 5185 * Make sure the connection in transaction is used for all database 5186 * interactions called by this method 5187 * 5188 * @param parentId the ID of the parent, or 0 if NULL 5189 * @param directoryName the name of the virtual directory to create 5190 * @param transaction the transaction in the scope of which the operation 5191 * is to be performed, managed by the caller 5192 * 5193 * @return a VirtualDirectory object representing the one added to the 5194 * database. 5195 * 5196 * @throws TskCoreException 5197 */ addVirtualDirectory(long parentId, String directoryName, CaseDbTransaction transaction)5198 public VirtualDirectory addVirtualDirectory(long parentId, String directoryName, CaseDbTransaction transaction) throws TskCoreException { 5199 if (transaction == null) { 5200 throw new TskCoreException("Passed null CaseDbTransaction"); 5201 } 5202 5203 transaction.acquireSingleUserCaseWriteLock(); 5204 ResultSet resultSet = null; 5205 try { 5206 // Get the parent path. 5207 CaseDbConnection connection = transaction.getConnection(); 5208 5209 String parentPath; 5210 Content parent = this.getAbstractFileById(parentId, connection); 5211 if (parent instanceof AbstractFile) { 5212 if (isRootDirectory((AbstractFile) parent, transaction)) { 5213 parentPath = "/"; 5214 } else { 5215 parentPath = ((AbstractFile) parent).getParentPath() + parent.getName() + "/"; //NON-NLS 5216 } 5217 } else { 5218 // The parent was either null or not an abstract file 5219 parentPath = "/"; 5220 } 5221 5222 // Insert a row for the virtual directory into the tsk_objects table. 5223 long newObjId = addObject(parentId, TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection); 5224 5225 // Insert a row for the virtual directory into the tsk_files table. 5226 // INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type, 5227 // dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, known, mime_type, parent_path, data_source_obj_id,extension) 5228 // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?) 5229 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE); 5230 statement.clearParameters(); 5231 statement.setLong(1, newObjId); 5232 5233 // If the parent is part of a file system, grab its file system ID 5234 if (0 != parentId) { 5235 long parentFs = this.getFileSystemId(parentId, connection); 5236 if (parentFs != -1) { 5237 statement.setLong(2, parentFs); 5238 } else { 5239 statement.setNull(2, java.sql.Types.BIGINT); 5240 } 5241 } else { 5242 statement.setNull(2, java.sql.Types.BIGINT); 5243 } 5244 5245 // name 5246 statement.setString(3, directoryName); 5247 5248 //type 5249 statement.setShort(4, TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()); 5250 statement.setShort(5, (short) 1); 5251 5252 //flags 5253 final TSK_FS_NAME_TYPE_ENUM dirType = TSK_FS_NAME_TYPE_ENUM.DIR; 5254 statement.setShort(6, dirType.getValue()); 5255 final TSK_FS_META_TYPE_ENUM metaType = TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR; 5256 statement.setShort(7, metaType.getValue()); 5257 5258 //allocated 5259 final TSK_FS_NAME_FLAG_ENUM dirFlag = TSK_FS_NAME_FLAG_ENUM.ALLOC; 5260 statement.setShort(8, dirFlag.getValue()); 5261 final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue() 5262 | TSK_FS_META_FLAG_ENUM.USED.getValue()); 5263 statement.setShort(9, metaFlags); 5264 5265 //size 5266 statement.setLong(10, 0); 5267 5268 // nulls for params 11-14 5269 statement.setNull(11, java.sql.Types.BIGINT); 5270 statement.setNull(12, java.sql.Types.BIGINT); 5271 statement.setNull(13, java.sql.Types.BIGINT); 5272 statement.setNull(14, java.sql.Types.BIGINT); 5273 5274 statement.setNull(15, java.sql.Types.VARCHAR); // MD5 5275 statement.setByte(16, FileKnown.UNKNOWN.getFileKnownValue()); // Known 5276 statement.setNull(17, java.sql.Types.VARCHAR); // MIME type 5277 5278 // parent path 5279 statement.setString(18, parentPath); 5280 5281 // data source object id (same as object id if this is a data source) 5282 long dataSourceObjectId; 5283 if (0 == parentId) { 5284 dataSourceObjectId = newObjId; 5285 } else { 5286 dataSourceObjectId = getDataSourceObjectId(connection, parentId); 5287 } 5288 statement.setLong(19, dataSourceObjectId); 5289 5290 //extension, since this is not really file we just set it to null 5291 statement.setString(20, null); 5292 connection.executeUpdate(statement); 5293 5294 return new VirtualDirectory(this, newObjId, dataSourceObjectId, directoryName, dirType, 5295 metaType, dirFlag, metaFlags, null, FileKnown.UNKNOWN, 5296 parentPath); 5297 } catch (SQLException e) { 5298 throw new TskCoreException("Error creating virtual directory '" + directoryName + "'", e); 5299 } finally { 5300 closeResultSet(resultSet); 5301 // NOTE: write lock will be released by transaction 5302 } 5303 } 5304 5305 /** 5306 * Adds a local directory to the database and returns a LocalDirectory 5307 * object representing it. 5308 * 5309 * @param parentId the ID of the parent, or 0 if NULL 5310 * @param directoryName the name of the local directory to create 5311 * 5312 * @return a LocalDirectory object representing the one added to the 5313 * database. 5314 * 5315 * @throws TskCoreException 5316 */ addLocalDirectory(long parentId, String directoryName)5317 public LocalDirectory addLocalDirectory(long parentId, String directoryName) throws TskCoreException { 5318 acquireSingleUserCaseWriteLock(); 5319 CaseDbTransaction localTrans = beginTransaction(); 5320 try { 5321 LocalDirectory newLD = addLocalDirectory(parentId, directoryName, localTrans); 5322 localTrans.commit(); 5323 return newLD; 5324 } catch (TskCoreException ex) { 5325 try { 5326 localTrans.rollback(); 5327 } catch (TskCoreException ex2) { 5328 logger.log(Level.SEVERE, String.format("Failed to rollback transaction after exception: %s", ex.getMessage()), ex2); 5329 } 5330 throw ex; 5331 } finally { 5332 releaseSingleUserCaseWriteLock(); 5333 } 5334 } 5335 5336 /** 5337 * Adds a local directory to the database and returns a LocalDirectory 5338 * object representing it. 5339 * 5340 * Make sure the connection in transaction is used for all database 5341 * interactions called by this method 5342 * 5343 * @param parentId the ID of the parent, or 0 if NULL 5344 * @param directoryName the name of the local directory to create 5345 * @param transaction the transaction in the scope of which the operation 5346 * is to be performed, managed by the caller 5347 * 5348 * @return a LocalDirectory object representing the one added to the 5349 * database. 5350 * 5351 * @throws TskCoreException 5352 */ addLocalDirectory(long parentId, String directoryName, CaseDbTransaction transaction)5353 public LocalDirectory addLocalDirectory(long parentId, String directoryName, CaseDbTransaction transaction) throws TskCoreException { 5354 if (transaction == null) { 5355 throw new TskCoreException("Passed null CaseDbTransaction"); 5356 } 5357 5358 transaction.acquireSingleUserCaseWriteLock(); 5359 ResultSet resultSet = null; 5360 try { 5361 // Get the parent path. 5362 CaseDbConnection connection = transaction.getConnection(); 5363 AbstractFile parent = getAbstractFileById(parentId, connection); 5364 String parentPath; 5365 if ((parent == null) || isRootDirectory(parent, transaction)) { 5366 parentPath = "/"; 5367 } else { 5368 parentPath = parent.getParentPath() + parent.getName() + "/"; //NON-NLS 5369 } 5370 5371 // Insert a row for the local directory into the tsk_objects table. 5372 long newObjId = addObject(parentId, TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection); 5373 5374 // Insert a row for the local directory into the tsk_files table. 5375 // INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type, 5376 // dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, known, mime_type, parent_path, data_source_obj_id) 5377 // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 5378 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE); 5379 statement.clearParameters(); 5380 statement.setLong(1, newObjId); 5381 5382 // The parent of a local directory will never be a file system 5383 statement.setNull(2, java.sql.Types.BIGINT); 5384 5385 // name 5386 statement.setString(3, directoryName); 5387 5388 //type 5389 statement.setShort(4, TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL_DIR.getFileType()); 5390 statement.setShort(5, (short) 1); 5391 5392 //flags 5393 final TSK_FS_NAME_TYPE_ENUM dirType = TSK_FS_NAME_TYPE_ENUM.DIR; 5394 statement.setShort(6, dirType.getValue()); 5395 final TSK_FS_META_TYPE_ENUM metaType = TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR; 5396 statement.setShort(7, metaType.getValue()); 5397 5398 //allocated 5399 final TSK_FS_NAME_FLAG_ENUM dirFlag = TSK_FS_NAME_FLAG_ENUM.ALLOC; 5400 statement.setShort(8, dirFlag.getValue()); 5401 final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue() 5402 | TSK_FS_META_FLAG_ENUM.USED.getValue()); 5403 statement.setShort(9, metaFlags); 5404 5405 //size 5406 statement.setLong(10, 0); 5407 5408 // nulls for params 11-14 5409 statement.setNull(11, java.sql.Types.BIGINT); 5410 statement.setNull(12, java.sql.Types.BIGINT); 5411 statement.setNull(13, java.sql.Types.BIGINT); 5412 statement.setNull(14, java.sql.Types.BIGINT); 5413 5414 statement.setNull(15, java.sql.Types.VARCHAR); // MD5 5415 statement.setByte(16, FileKnown.UNKNOWN.getFileKnownValue()); // Known 5416 statement.setNull(17, java.sql.Types.VARCHAR); // MIME type 5417 5418 // parent path 5419 statement.setString(18, parentPath); 5420 5421 // data source object id 5422 long dataSourceObjectId = getDataSourceObjectId(connection, parentId); 5423 statement.setLong(19, dataSourceObjectId); 5424 5425 //extension, since this is a directory we just set it to null 5426 statement.setString(20, null); 5427 5428 connection.executeUpdate(statement); 5429 5430 return new LocalDirectory(this, newObjId, dataSourceObjectId, directoryName, dirType, 5431 metaType, dirFlag, metaFlags, null, FileKnown.UNKNOWN, 5432 parentPath); 5433 } catch (SQLException e) { 5434 throw new TskCoreException("Error creating local directory '" + directoryName + "'", e); 5435 } finally { 5436 closeResultSet(resultSet); 5437 // NOTE: write lock will be released by transaction 5438 } 5439 } 5440 5441 /** 5442 * Adds a local/logical files and/or directories data source. 5443 * 5444 * @param deviceId An ASCII-printable identifier for the device 5445 * associated with the data source that is intended 5446 * to be unique across multiple cases (e.g., a 5447 * UUID). 5448 * @param rootDirectoryName The name for the root virtual directory for the 5449 * data source. 5450 * @param timeZone The time zone used to process the data source, 5451 * may be the empty string. 5452 * @param transaction A transaction in the scope of which the 5453 * operation is to be performed, managed by the 5454 * caller. 5455 * 5456 * @return The new local files data source. 5457 * 5458 * @throws TskCoreException if there is an error adding the data source. 5459 */ addLocalFilesDataSource(String deviceId, String rootDirectoryName, String timeZone, CaseDbTransaction transaction)5460 public LocalFilesDataSource addLocalFilesDataSource(String deviceId, String rootDirectoryName, String timeZone, CaseDbTransaction transaction) throws TskCoreException { 5461 acquireSingleUserCaseWriteLock(); 5462 Statement statement = null; 5463 try { 5464 // Insert a row for the root virtual directory of the data source 5465 // into the tsk_objects table. 5466 CaseDbConnection connection = transaction.getConnection(); 5467 long newObjId = addObject(0, TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection); 5468 5469 // Insert a row for the virtual directory of the data source into 5470 // the data_source_info table. 5471 statement = connection.createStatement(); 5472 statement.executeUpdate("INSERT INTO data_source_info (obj_id, device_id, time_zone) " 5473 + "VALUES(" + newObjId + ", '" + deviceId + "', '" + timeZone + "');"); 5474 5475 // Insert a row for the root virtual directory of the data source 5476 // into the tsk_files table. Note that its data source object id is 5477 // its own object id. 5478 // INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, 5479 // dir_type, meta_type, dir_flags, meta_flags, size, ctime, crtime, 5480 // atime, mtime, md5, known, mime_type, parent_path, data_source_obj_id, extension) 5481 // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?) 5482 PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE); 5483 preparedStatement.clearParameters(); 5484 preparedStatement.setLong(1, newObjId); 5485 preparedStatement.setNull(2, java.sql.Types.BIGINT); 5486 preparedStatement.setString(3, rootDirectoryName); 5487 preparedStatement.setShort(4, TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()); 5488 preparedStatement.setShort(5, (short) 1); 5489 TSK_FS_NAME_TYPE_ENUM dirType = TSK_FS_NAME_TYPE_ENUM.DIR; 5490 preparedStatement.setShort(6, TSK_FS_NAME_TYPE_ENUM.DIR.getValue()); 5491 TSK_FS_META_TYPE_ENUM metaType = TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR; 5492 preparedStatement.setShort(7, metaType.getValue()); 5493 TSK_FS_NAME_FLAG_ENUM dirFlag = TSK_FS_NAME_FLAG_ENUM.ALLOC; 5494 preparedStatement.setShort(8, dirFlag.getValue()); 5495 final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue() 5496 | TSK_FS_META_FLAG_ENUM.USED.getValue()); 5497 preparedStatement.setShort(9, metaFlags); 5498 preparedStatement.setLong(10, 0); 5499 preparedStatement.setNull(11, java.sql.Types.BIGINT); 5500 preparedStatement.setNull(12, java.sql.Types.BIGINT); 5501 preparedStatement.setNull(13, java.sql.Types.BIGINT); 5502 preparedStatement.setNull(14, java.sql.Types.BIGINT); 5503 preparedStatement.setNull(15, java.sql.Types.VARCHAR); // MD5 5504 preparedStatement.setByte(16, FileKnown.UNKNOWN.getFileKnownValue()); // Known 5505 preparedStatement.setNull(17, java.sql.Types.VARCHAR); // MIME type 5506 String parentPath = "/"; //NON-NLS 5507 preparedStatement.setString(18, parentPath); 5508 preparedStatement.setLong(19, newObjId); 5509 preparedStatement.setString(20, null); //extension, just set it to null 5510 connection.executeUpdate(preparedStatement); 5511 5512 return new LocalFilesDataSource(this, newObjId, newObjId, deviceId, rootDirectoryName, dirType, metaType, dirFlag, metaFlags, timeZone, null, FileKnown.UNKNOWN, parentPath); 5513 5514 } catch (SQLException ex) { 5515 throw new TskCoreException(String.format("Error creating local files data source with device id %s and directory name %s", deviceId, rootDirectoryName), ex); 5516 } finally { 5517 closeStatement(statement); 5518 releaseSingleUserCaseWriteLock(); 5519 } 5520 } 5521 5522 /** 5523 * Add an image to the database. 5524 * 5525 * @param type Type of image 5526 * @param sectorSize Sector size 5527 * @param size Image size 5528 * @param displayName Display name for the image 5529 * @param imagePaths Image path(s) 5530 * @param timezone Time zone 5531 * @param md5 MD5 hash 5532 * @param sha1 SHA1 hash 5533 * @param sha256 SHA256 hash 5534 * @param deviceId Device ID 5535 * @param transaction Case DB transaction 5536 * 5537 * @return the newly added Image 5538 * 5539 * @throws TskCoreException 5540 */ addImage(TskData.TSK_IMG_TYPE_ENUM type, long sectorSize, long size, String displayName, List<String> imagePaths, String timezone, String md5, String sha1, String sha256, String deviceId, CaseDbTransaction transaction)5541 public Image addImage(TskData.TSK_IMG_TYPE_ENUM type, long sectorSize, long size, String displayName, List<String> imagePaths, 5542 String timezone, String md5, String sha1, String sha256, 5543 String deviceId, 5544 CaseDbTransaction transaction) throws TskCoreException { 5545 acquireSingleUserCaseWriteLock(); 5546 Statement statement = null; 5547 try { 5548 // Insert a row for the Image into the tsk_objects table. 5549 CaseDbConnection connection = transaction.getConnection(); 5550 long newObjId = addObject(0, TskData.ObjectType.IMG.getObjectType(), connection); 5551 5552 // Add a row to tsk_image_info 5553 // INSERT INTO tsk_image_info (obj_id, type, ssize, tzone, size, md5, sha1, sha256, display_name) 5554 PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_IMAGE_INFO); 5555 preparedStatement.clearParameters(); 5556 preparedStatement.setLong(1, newObjId); 5557 preparedStatement.setShort(2, (short) type.getValue()); 5558 preparedStatement.setLong(3, sectorSize); 5559 preparedStatement.setString(4, timezone); 5560 //prevent negative size 5561 long savedSize = size < 0 ? 0 : size; 5562 preparedStatement.setLong(5, savedSize); 5563 preparedStatement.setString(6, md5); 5564 preparedStatement.setString(7, sha1); 5565 preparedStatement.setString(8, sha256); 5566 preparedStatement.setString(9, displayName); 5567 connection.executeUpdate(preparedStatement); 5568 5569 // If there are paths, add them to tsk_image_names 5570 for (int i = 0; i < imagePaths.size(); i++) { 5571 preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_IMAGE_NAME); 5572 preparedStatement.clearParameters(); 5573 preparedStatement.setLong(1, newObjId); 5574 preparedStatement.setString(2, imagePaths.get(i)); 5575 preparedStatement.setLong(3, i); 5576 connection.executeUpdate(preparedStatement); 5577 } 5578 5579 // Add a row to data_source_info 5580 preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_DATA_SOURCE_INFO); 5581 statement = connection.createStatement(); 5582 preparedStatement.setLong(1, newObjId); 5583 preparedStatement.setString(2, deviceId); 5584 preparedStatement.setString(3, timezone); 5585 connection.executeUpdate(preparedStatement); 5586 5587 // Create the new Image object 5588 return new Image(this, newObjId, type.getValue(), deviceId, sectorSize, displayName, 5589 imagePaths.toArray(new String[imagePaths.size()]), timezone, md5, sha1, sha256, savedSize); 5590 } catch (SQLException ex) { 5591 if (!imagePaths.isEmpty()) { 5592 throw new TskCoreException(String.format("Error adding image with path %s to database", imagePaths.get(0)), ex); 5593 } else { 5594 throw new TskCoreException(String.format("Error adding image with display name %s to database", displayName), ex); 5595 } 5596 } finally { 5597 closeStatement(statement); 5598 releaseSingleUserCaseWriteLock(); 5599 } 5600 } 5601 5602 /** 5603 * Add a volume system to the database. 5604 * 5605 * @param parentObjId Object ID of the volume system's parent 5606 * @param type Type of volume system 5607 * @param imgOffset Image offset 5608 * @param blockSize Block size 5609 * @param transaction Case DB transaction 5610 * 5611 * @return the newly added VolumeSystem 5612 * 5613 * @throws TskCoreException 5614 */ 5615 public VolumeSystem addVolumeSystem(long parentObjId, TskData.TSK_VS_TYPE_ENUM type, long imgOffset, 5616 long blockSize, CaseDbTransaction transaction) throws TskCoreException { 5617 acquireSingleUserCaseWriteLock(); 5618 try { 5619 // Insert a row for the VolumeSystem into the tsk_objects table. 5620 CaseDbConnection connection = transaction.getConnection(); 5621 long newObjId = addObject(parentObjId, TskData.ObjectType.VS.getObjectType(), connection); 5622 5623 // Add a row to tsk_vs_info 5624 // INSERT INTO tsk_vs_info (obj_id, vs_type, img_offset, block_size) 5625 PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_VS_INFO); 5626 preparedStatement.clearParameters(); 5627 preparedStatement.setLong(1, newObjId); 5628 preparedStatement.setShort(2, (short) type.getVsType()); 5629 preparedStatement.setLong(3, imgOffset); 5630 preparedStatement.setLong(4, blockSize); 5631 connection.executeUpdate(preparedStatement); 5632 5633 // Create the new VolumeSystem object 5634 return new VolumeSystem(this, newObjId, "", type.getVsType(), imgOffset, blockSize); 5635 } catch (SQLException ex) { 5636 throw new TskCoreException(String.format("Error creating volume system with parent ID %d and image offset %d", 5637 parentObjId, imgOffset), ex); 5638 } finally { 5639 releaseSingleUserCaseWriteLock(); 5640 } 5641 } 5642 5643 /** 5644 * Add a volume to the database 5645 * 5646 * @param parentObjId Object ID of the volume's parent 5647 * @param addr Address of the volume 5648 * @param start Start of the volume 5649 * @param length Length of the volume 5650 * @param desc Description of the volume 5651 * @param flags Flags 5652 * @param transaction Case DB transaction 5653 * 5654 * @return the newly created Volume 5655 * 5656 * @throws TskCoreException 5657 */ 5658 public Volume addVolume(long parentObjId, long addr, long start, long length, String desc, 5659 long flags, CaseDbTransaction transaction) throws TskCoreException { 5660 acquireSingleUserCaseWriteLock(); 5661 Statement statement = null; 5662 try { 5663 // Insert a row for the Volume into the tsk_objects table. 5664 CaseDbConnection connection = transaction.getConnection(); 5665 long newObjId = addObject(parentObjId, TskData.ObjectType.VOL.getObjectType(), connection); 5666 5667 // Add a row to tsk_vs_parts 5668 // INSERT INTO tsk_vs_parts (obj_id, addr, start, length, desc, flags) 5669 PreparedStatement preparedStatement; 5670 if (this.dbType == DbType.POSTGRESQL) { 5671 preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_VS_PART_POSTGRESQL); 5672 } else { 5673 preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_VS_PART_SQLITE); 5674 } 5675 preparedStatement.clearParameters(); 5676 preparedStatement.setLong(1, newObjId); 5677 preparedStatement.setLong(2, addr); 5678 preparedStatement.setLong(3, start); 5679 preparedStatement.setLong(4, length); 5680 preparedStatement.setString(5, desc); 5681 preparedStatement.setShort(6, (short) flags); 5682 connection.executeUpdate(preparedStatement); 5683 5684 // Create the new Volume object 5685 return new Volume(this, newObjId, addr, start, length, flags, desc); 5686 } catch (SQLException ex) { 5687 throw new TskCoreException(String.format("Error creating volume with address %d and parent ID %d", addr, parentObjId), ex); 5688 } finally { 5689 closeStatement(statement); 5690 releaseSingleUserCaseWriteLock(); 5691 } 5692 } 5693 5694 /** 5695 * Add a FileSystem to the database. 5696 * 5697 * @param parentObjId Object ID of the file system's parent 5698 * @param imgOffset Offset in the image 5699 * @param type Type of file system 5700 * @param blockSize Block size 5701 * @param blockCount Block count 5702 * @param rootInum root inum 5703 * @param firstInum first inum 5704 * @param lastInum last inum 5705 * @param displayName display name 5706 * @param transaction Case DB transaction 5707 * 5708 * @return the newly created FileSystem 5709 * 5710 * @throws TskCoreException 5711 */ 5712 public FileSystem addFileSystem(long parentObjId, long imgOffset, TskData.TSK_FS_TYPE_ENUM type, long blockSize, long blockCount, 5713 long rootInum, long firstInum, long lastInum, String displayName, 5714 CaseDbTransaction transaction) throws TskCoreException { 5715 acquireSingleUserCaseWriteLock(); 5716 Statement statement = null; 5717 try { 5718 // Insert a row for the FileSystem into the tsk_objects table. 5719 CaseDbConnection connection = transaction.getConnection(); 5720 long newObjId = addObject(parentObjId, TskData.ObjectType.FS.getObjectType(), connection); 5721 5722 // Add a row to tsk_fs_info 5723 // INSERT INTO tsk_fs_info (obj_id, img_offset, fs_type, block_size, block_count, root_inum, first_inum, last_inum, display_name) 5724 PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FS_INFO); 5725 preparedStatement.clearParameters(); 5726 preparedStatement.setLong(1, newObjId); 5727 preparedStatement.setLong(2, imgOffset); 5728 preparedStatement.setShort(3, (short) type.getValue()); 5729 preparedStatement.setLong(4, blockSize); 5730 preparedStatement.setLong(5, blockCount); 5731 preparedStatement.setLong(6, rootInum); 5732 preparedStatement.setLong(7, firstInum); 5733 preparedStatement.setLong(8, lastInum); 5734 preparedStatement.setString(9, displayName); 5735 connection.executeUpdate(preparedStatement); 5736 5737 // Create the new FileSystem object 5738 return new FileSystem(this, newObjId, displayName, imgOffset, type, blockSize, blockCount, rootInum, 5739 firstInum, lastInum); 5740 } catch (SQLException ex) { 5741 throw new TskCoreException(String.format("Error creating file system with image offset %d and parent ID %d", 5742 imgOffset, parentObjId), ex); 5743 } finally { 5744 closeStatement(statement); 5745 releaseSingleUserCaseWriteLock(); 5746 } 5747 } 5748 5749 /** 5750 * Add a file system file. 5751 * 5752 * @param dataSourceObjId The object id of the root data source of this 5753 * file. 5754 * @param fsObjId The file system object id. 5755 * @param fileName The name of the file. 5756 * @param metaAddr The meta address of the file. 5757 * @param metaSeq The meta address sequence of the file. 5758 * @param attrType The attributed type of the file. 5759 * @param attrId The attribute id 5760 * @param dirFlag The allocated status from the name structure 5761 * @param metaFlags 5762 * @param size The size of the file in bytes. 5763 * @param ctime The changed time of the file. 5764 * @param crtime The creation time of the file. 5765 * @param atime The accessed time of the file 5766 * @param mtime The modified time of the file. 5767 ** @param isFile True, unless the file is a directory. 5768 * @param parent The parent of the file (e.g., a virtual directory) 5769 * 5770 * @return Newly created file 5771 * 5772 * @throws TskCoreException 5773 */ 5774 public FsContent addFileSystemFile(long dataSourceObjId, long fsObjId, 5775 String fileName, 5776 long metaAddr, int metaSeq, 5777 TSK_FS_ATTR_TYPE_ENUM attrType, int attrId, 5778 TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size, 5779 long ctime, long crtime, long atime, long mtime, 5780 boolean isFile, Content parent) throws TskCoreException { 5781 5782 CaseDbTransaction transaction = beginTransaction(); 5783 Statement queryStatement = null; 5784 try { 5785 CaseDbConnection connection = transaction.getConnection(); 5786 transaction.acquireSingleUserCaseWriteLock(); 5787 5788 // Insert a row for the local/logical file into the tsk_objects table. 5789 // INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?) 5790 long objectId = addObject(parent.getId(), TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection); 5791 5792 String parentPath; 5793 5794 if (parent instanceof AbstractFile) { 5795 AbstractFile parentFile = (AbstractFile) parent; 5796 if (isRootDirectory(parentFile, transaction)) { 5797 parentPath = "/"; 5798 } else { 5799 parentPath = parentFile.getParentPath() + parent.getName() + "/"; //NON-NLS 5800 } 5801 } else { 5802 parentPath = "/"; 5803 } 5804 5805 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE_SYSTEM_FILE); 5806 statement.clearParameters(); 5807 statement.setLong(1, objectId); // obj_is 5808 statement.setLong(2, fsObjId); // fs_obj_id 5809 statement.setLong(3, dataSourceObjId); // data_source_obj_id 5810 statement.setShort(4, (short)attrType.getValue()); // attr_type 5811 statement.setInt(5, attrId); // attr_id 5812 statement.setString(6, fileName); // name 5813 statement.setLong(7, metaAddr); // meta_addr 5814 statement.setInt(8, metaSeq); // meta_addr 5815 statement.setShort(9, TskData.TSK_DB_FILES_TYPE_ENUM.FS.getFileType()); //type 5816 statement.setShort(10, (short) 1); // has_path 5817 TSK_FS_NAME_TYPE_ENUM dirType = isFile ? TSK_FS_NAME_TYPE_ENUM.REG : TSK_FS_NAME_TYPE_ENUM.DIR; 5818 statement.setShort(11, dirType.getValue()); // dir_type 5819 TSK_FS_META_TYPE_ENUM metaType = isFile ? TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG : TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR; 5820 statement.setShort(12, metaType.getValue()); // meta_type 5821 statement.setShort(13, dirFlag.getValue()); // dir_flags 5822 statement.setShort(14, metaFlags); // meta_flags 5823 statement.setLong(15, size < 0 ? 0 : size); 5824 statement.setLong(16, ctime); 5825 statement.setLong(17, crtime); 5826 statement.setLong(18, atime); 5827 statement.setLong(19, mtime); 5828 statement.setString(20, parentPath); 5829 final String extension = extractExtension(fileName); 5830 statement.setString(21, extension); 5831 5832 connection.executeUpdate(statement); 5833 5834 transaction.commit(); 5835 transaction = null; 5836 5837 return new org.sleuthkit.datamodel.File(this, objectId, dataSourceObjId, fsObjId, 5838 attrType, attrId, fileName, metaAddr, metaSeq, 5839 dirType, metaType, dirFlag, metaFlags, 5840 size, ctime, crtime, atime, mtime, 5841 (short)0, 0, 0, null, null, parentPath, null, 5842 extension); 5843 5844 } catch(SQLException ex) { 5845 logger.log(Level.WARNING, "Failed to add file system file", ex); 5846 } 5847 finally { 5848 closeStatement(queryStatement); 5849 if (null != transaction) { 5850 try { 5851 transaction.rollback(); 5852 } catch (TskCoreException ex2) { 5853 logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex2); 5854 } 5855 } 5856 } 5857 return null; 5858 } 5859 5860 /** 5861 * Get IDs of the virtual folder roots (at the same level as image), used 5862 * for containers such as for local files. 5863 * 5864 * @return IDs of virtual directory root objects. 5865 * 5866 * @throws org.sleuthkit.datamodel.TskCoreException 5867 */ 5868 public List<VirtualDirectory> getVirtualDirectoryRoots() throws TskCoreException { 5869 CaseDbConnection connection = connections.getConnection(); 5870 acquireSingleUserCaseReadLock(); 5871 Statement s = null; 5872 ResultSet rs = null; 5873 try { 5874 s = connection.createStatement(); 5875 rs = connection.executeQuery(s, "SELECT * FROM tsk_files WHERE" //NON-NLS 5876 + " type = " + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() 5877 + " AND obj_id = data_source_obj_id" 5878 + " ORDER BY dir_type, LOWER(name)"); //NON-NLS 5879 List<VirtualDirectory> virtDirRootIds = new ArrayList<VirtualDirectory>(); 5880 while (rs.next()) { 5881 virtDirRootIds.add(virtualDirectory(rs, connection)); 5882 } 5883 return virtDirRootIds; 5884 } catch (SQLException ex) { 5885 throw new TskCoreException("Error getting local files virtual folder id", ex); 5886 } finally { 5887 closeResultSet(rs); 5888 closeStatement(s); 5889 connection.close(); 5890 releaseSingleUserCaseReadLock(); 5891 } 5892 } 5893 5894 /** 5895 * Adds one or more layout files for a parent Content object to the case 5896 * database. 5897 * 5898 * @param parent The parent Content. 5899 * @param fileRanges File range objects for the file(s). 5900 * 5901 * @return A list of LayoutFile objects. 5902 * 5903 * @throws TskCoreException If there is a problem completing a case database 5904 * operation. 5905 */ 5906 public final List<LayoutFile> addLayoutFiles(Content parent, List<TskFileRange> fileRanges) throws TskCoreException { 5907 assert (null != fileRanges); 5908 if (null == fileRanges) { 5909 throw new TskCoreException("TskFileRange object is null"); 5910 } 5911 5912 assert (null != parent); 5913 if (null == parent) { 5914 throw new TskCoreException("Conent is null"); 5915 } 5916 5917 CaseDbTransaction transaction = null; 5918 Statement statement = null; 5919 ResultSet resultSet = null; 5920 5921 try { 5922 transaction = beginTransaction(); 5923 transaction.acquireSingleUserCaseWriteLock(); 5924 CaseDbConnection connection = transaction.getConnection(); 5925 5926 List<LayoutFile> fileRangeLayoutFiles = new ArrayList<LayoutFile>(); 5927 for (TskFileRange fileRange : fileRanges) { 5928 /* 5929 * Insert a row for the Tsk file range into the tsk_objects 5930 * table: INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, 5931 * ?) 5932 */ 5933 long fileRangeId = addObject(parent.getId(), TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection); 5934 long end_byte_in_parent = fileRange.getByteStart() + fileRange.getByteLen() - 1; 5935 /* 5936 * Insert a row for the Tsk file range into the tsk_files table: 5937 * INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, 5938 * has_path, dir_type, meta_type, dir_flags, meta_flags, size, 5939 * ctime, crtime, atime, mtime, md5, known, mime_type, 5940 * parent_path, data_source_obj_id,extension) VALUES (?, ?, ?, 5941 * ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?) 5942 */ 5943 PreparedStatement prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE); 5944 prepStmt.clearParameters(); 5945 prepStmt.setLong(1, fileRangeId); // obj_id from tsk_objects 5946 prepStmt.setNull(2, java.sql.Types.BIGINT); // fs_obj_id 5947 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] 5948 prepStmt.setShort(4, TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType()); // type 5949 prepStmt.setNull(5, java.sql.Types.BIGINT); // has_path 5950 prepStmt.setShort(6, TSK_FS_NAME_TYPE_ENUM.REG.getValue()); // dir_type 5951 prepStmt.setShort(7, TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue()); // meta_type 5952 prepStmt.setShort(8, TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue()); // dir_flags 5953 prepStmt.setShort(9, TSK_FS_META_FLAG_ENUM.UNALLOC.getValue()); // nmeta_flags 5954 prepStmt.setLong(10, fileRange.getByteLen()); // size 5955 prepStmt.setNull(11, java.sql.Types.BIGINT); // ctime 5956 prepStmt.setNull(12, java.sql.Types.BIGINT); // crtime 5957 prepStmt.setNull(13, java.sql.Types.BIGINT); // atime 5958 prepStmt.setNull(14, java.sql.Types.BIGINT); // mtime 5959 prepStmt.setNull(15, java.sql.Types.VARCHAR); // MD5 5960 prepStmt.setByte(16, FileKnown.UNKNOWN.getFileKnownValue()); // Known 5961 prepStmt.setNull(17, java.sql.Types.VARCHAR); // MIME type 5962 prepStmt.setNull(18, java.sql.Types.VARCHAR); // parent path 5963 prepStmt.setLong(19, parent.getId()); // data_source_obj_id 5964 5965 //extension, since this is not a FS file we just set it to null 5966 prepStmt.setString(20, null); 5967 connection.executeUpdate(prepStmt); 5968 5969 /* 5970 * Insert a row in the tsk_layout_file table for each chunk of 5971 * the carved file. INSERT INTO tsk_file_layout (obj_id, 5972 * byte_start, byte_len, sequence) VALUES (?, ?, ?, ?) 5973 */ 5974 prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LAYOUT_FILE); 5975 prepStmt.clearParameters(); 5976 prepStmt.setLong(1, fileRangeId); // obj_id 5977 prepStmt.setLong(2, fileRange.getByteStart()); // byte_start 5978 prepStmt.setLong(3, fileRange.getByteLen()); // byte_len 5979 prepStmt.setLong(4, fileRange.getSequence()); // sequence 5980 connection.executeUpdate(prepStmt); 5981 5982 /* 5983 * Create a layout file representation of the carved file. 5984 */ 5985 fileRangeLayoutFiles.add(new LayoutFile(this, 5986 fileRangeId, 5987 parent.getId(), 5988 Long.toString(fileRange.getSequence()), 5989 TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS, 5990 TSK_FS_NAME_TYPE_ENUM.REG, 5991 TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG, 5992 TSK_FS_NAME_FLAG_ENUM.UNALLOC, 5993 TSK_FS_META_FLAG_ENUM.UNALLOC.getValue(), 5994 fileRange.getByteLen(), 5995 0L, 0L, 0L, 0L, 5996 null, 5997 FileKnown.UNKNOWN, 5998 parent.getUniquePath(), 5999 null)); 6000 } 6001 6002 transaction.commit(); 6003 transaction = null; 6004 return fileRangeLayoutFiles; 6005 6006 } catch (SQLException ex) { 6007 throw new TskCoreException("Failed to add layout files to case database", ex); 6008 } finally { 6009 closeResultSet(resultSet); 6010 closeStatement(statement); 6011 6012 // NOTE: write lock will be released by transaction 6013 if (null != transaction) { 6014 try { 6015 transaction.rollback(); 6016 } catch (TskCoreException ex2) { 6017 logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex2); 6018 } 6019 } 6020 } 6021 } 6022 6023 /** 6024 * Adds a carving result to the case database. 6025 * 6026 * @param carvingResult The carving result (a set of carved files and their 6027 * parent) to be added. 6028 * 6029 * @return A list of LayoutFile representations of the carved files. 6030 * 6031 * @throws TskCoreException If there is a problem completing a case database 6032 * operation. 6033 */ 6034 public final List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws TskCoreException { 6035 assert (null != carvingResult); 6036 if (null == carvingResult) { 6037 throw new TskCoreException("Carving is null"); 6038 } 6039 assert (null != carvingResult.getParent()); 6040 if (null == carvingResult.getParent()) { 6041 throw new TskCoreException("Carving result has null parent"); 6042 } 6043 assert (null != carvingResult.getCarvedFiles()); 6044 if (null == carvingResult.getCarvedFiles()) { 6045 throw new TskCoreException("Carving result has null carved files"); 6046 } 6047 CaseDbTransaction transaction = null; 6048 Statement statement = null; 6049 ResultSet resultSet = null; 6050 long newCacheKey = 0; // Used to roll back cache if transaction is rolled back. 6051 try { 6052 transaction = beginTransaction(); 6053 transaction.acquireSingleUserCaseWriteLock(); 6054 CaseDbConnection connection = transaction.getConnection(); 6055 6056 /* 6057 * Carved files are "re-parented" as children of the $CarvedFiles 6058 * virtual directory of the root file system, volume, or image 6059 * ancestor of the carved files parent, but if no such ancestor is 6060 * found, then the parent specified in the carving result is used. 6061 */ 6062 Content root = carvingResult.getParent(); 6063 while (null != root) { 6064 if (root instanceof FileSystem || root instanceof Volume || root instanceof Image) { 6065 break; 6066 } 6067 root = root.getParent(); 6068 } 6069 if (null == root) { 6070 root = carvingResult.getParent(); 6071 } 6072 6073 /* 6074 * Get or create the $CarvedFiles virtual directory for the root 6075 * ancestor. 6076 */ 6077 VirtualDirectory carvedFilesDir = rootIdsToCarvedFileDirs.get(root.getId()); 6078 if (null == carvedFilesDir) { 6079 List<Content> rootChildren; 6080 if (root instanceof FileSystem) { 6081 rootChildren = ((FileSystem) root).getRootDirectory().getChildren(); 6082 } else { 6083 rootChildren = root.getChildren(); 6084 } 6085 for (Content child : rootChildren) { 6086 if (child instanceof VirtualDirectory && child.getName().equals(VirtualDirectory.NAME_CARVED)) { 6087 carvedFilesDir = (VirtualDirectory) child; 6088 break; 6089 } 6090 } 6091 if (null == carvedFilesDir) { 6092 long parId = root.getId(); 6093 // $CarvedFiles should be a child of the root directory, not the file system 6094 if (root instanceof FileSystem) { 6095 Content rootDir = ((FileSystem) root).getRootDirectory(); 6096 parId = rootDir.getId(); 6097 } 6098 carvedFilesDir = addVirtualDirectory(parId, VirtualDirectory.NAME_CARVED, transaction); 6099 } 6100 newCacheKey = root.getId(); 6101 rootIdsToCarvedFileDirs.put(newCacheKey, carvedFilesDir); 6102 } 6103 6104 /* 6105 * Add the carved files to the database as children of the 6106 * $CarvedFile directory of the root ancestor. 6107 */ 6108 String parentPath = getFileParentPath(carvedFilesDir.getId(), connection) + carvedFilesDir.getName() + "/"; 6109 List<LayoutFile> carvedFiles = new ArrayList<LayoutFile>(); 6110 for (CarvingResult.CarvedFile carvedFile : carvingResult.getCarvedFiles()) { 6111 /* 6112 * Insert a row for the carved file into the tsk_objects table: 6113 * INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?) 6114 */ 6115 long carvedFileId = addObject(carvedFilesDir.getId(), TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection); 6116 6117 /* 6118 * Insert a row for the carved file into the tsk_files table: 6119 * INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, 6120 * has_path, dir_type, meta_type, dir_flags, meta_flags, size, 6121 * ctime, crtime, atime, mtime, md5, known, mime_type, 6122 * parent_path, data_source_obj_id,extenion) VALUES (?, ?, ?, ?, 6123 * ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?) 6124 */ 6125 PreparedStatement prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE); 6126 prepStmt.clearParameters(); 6127 prepStmt.setLong(1, carvedFileId); // obj_id 6128 if (root instanceof FileSystem) { 6129 prepStmt.setLong(2, root.getId()); // fs_obj_id 6130 } else { 6131 prepStmt.setNull(2, java.sql.Types.BIGINT); // fs_obj_id 6132 } 6133 prepStmt.setString(3, carvedFile.getName()); // name 6134 prepStmt.setShort(4, TSK_DB_FILES_TYPE_ENUM.CARVED.getFileType()); // type 6135 prepStmt.setShort(5, (short) 1); // has_path 6136 prepStmt.setShort(6, TSK_FS_NAME_TYPE_ENUM.REG.getValue()); // dir_type 6137 prepStmt.setShort(7, TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue()); // meta_type 6138 prepStmt.setShort(8, TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue()); // dir_flags 6139 prepStmt.setShort(9, TSK_FS_META_FLAG_ENUM.UNALLOC.getValue()); // nmeta_flags 6140 prepStmt.setLong(10, carvedFile.getSizeInBytes()); // size 6141 prepStmt.setNull(11, java.sql.Types.BIGINT); // ctime 6142 prepStmt.setNull(12, java.sql.Types.BIGINT); // crtime 6143 prepStmt.setNull(13, java.sql.Types.BIGINT); // atime 6144 prepStmt.setNull(14, java.sql.Types.BIGINT); // mtime 6145 prepStmt.setNull(15, java.sql.Types.VARCHAR); // MD5 6146 prepStmt.setByte(16, FileKnown.UNKNOWN.getFileKnownValue()); // Known 6147 prepStmt.setNull(17, java.sql.Types.VARCHAR); // MIME type 6148 prepStmt.setString(18, parentPath); // parent path 6149 prepStmt.setLong(19, carvedFilesDir.getDataSourceObjectId()); // data_source_obj_id 6150 prepStmt.setString(20, extractExtension(carvedFile.getName())); //extension 6151 connection.executeUpdate(prepStmt); 6152 6153 /* 6154 * Insert a row in the tsk_layout_file table for each chunk of 6155 * the carved file. INSERT INTO tsk_file_layout (obj_id, 6156 * byte_start, byte_len, sequence) VALUES (?, ?, ?, ?) 6157 */ 6158 prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LAYOUT_FILE); 6159 for (TskFileRange tskFileRange : carvedFile.getLayoutInParent()) { 6160 prepStmt.clearParameters(); 6161 prepStmt.setLong(1, carvedFileId); // obj_id 6162 prepStmt.setLong(2, tskFileRange.getByteStart()); // byte_start 6163 prepStmt.setLong(3, tskFileRange.getByteLen()); // byte_len 6164 prepStmt.setLong(4, tskFileRange.getSequence()); // sequence 6165 connection.executeUpdate(prepStmt); 6166 } 6167 6168 /* 6169 * Create a layout file representation of the carved file. 6170 */ 6171 carvedFiles.add(new LayoutFile(this, 6172 carvedFileId, 6173 carvedFilesDir.getDataSourceObjectId(), 6174 carvedFile.getName(), 6175 TSK_DB_FILES_TYPE_ENUM.CARVED, 6176 TSK_FS_NAME_TYPE_ENUM.REG, 6177 TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG, 6178 TSK_FS_NAME_FLAG_ENUM.UNALLOC, 6179 TSK_FS_META_FLAG_ENUM.UNALLOC.getValue(), 6180 carvedFile.getSizeInBytes(), 6181 0L, 0L, 0L, 0L, 6182 null, 6183 FileKnown.UNKNOWN, 6184 parentPath, 6185 null)); 6186 } 6187 6188 transaction.commit(); 6189 transaction = null; 6190 return carvedFiles; 6191 6192 } catch (SQLException ex) { 6193 throw new TskCoreException("Failed to add carved files to case database", ex); 6194 } finally { 6195 closeResultSet(resultSet); 6196 closeStatement(statement); 6197 6198 // NOTE: write lock will be released by transaction 6199 if (null != transaction) { 6200 try { 6201 transaction.rollback(); 6202 } catch (TskCoreException ex2) { 6203 logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex2); 6204 } 6205 if (0 != newCacheKey) { 6206 rootIdsToCarvedFileDirs.remove(newCacheKey); 6207 } 6208 } 6209 } 6210 } 6211 6212 /** 6213 * Creates a new derived file object, adds it to database and returns it. 6214 * 6215 * TODO add support for adding derived method 6216 * 6217 * @param fileName file name the derived file 6218 * @param localPath local path of the derived file, including the file 6219 * name. The path is relative to the database path. 6220 * @param size size of the derived file in bytes 6221 * @param ctime The changed time of the file. 6222 * @param crtime The creation time of the file. 6223 * @param atime The accessed time of the file 6224 * @param mtime The modified time of the file. 6225 * @param isFile whether a file or directory, true if a file 6226 * @param parentObj parent content object 6227 * @param rederiveDetails details needed to re-derive file (will be specific 6228 * to the derivation method), currently unused 6229 * @param toolName name of derivation method/tool, currently unused 6230 * @param toolVersion version of derivation method/tool, currently 6231 * unused 6232 * @param otherDetails details of derivation method/tool, currently 6233 * unused 6234 * @param encodingType Type of encoding used on the file (or NONE if no 6235 * encoding) 6236 * 6237 * @return newly created derived file object 6238 * 6239 * @throws TskCoreException exception thrown if the object creation failed 6240 * due to a critical system error 6241 */ 6242 public DerivedFile addDerivedFile(String fileName, String localPath, 6243 long size, long ctime, long crtime, long atime, long mtime, 6244 boolean isFile, Content parentObj, 6245 String rederiveDetails, String toolName, String toolVersion, 6246 String otherDetails, TskData.EncodingType encodingType) throws TskCoreException { 6247 // Strip off any leading slashes from the local path (leading slashes indicate absolute paths) 6248 localPath = localPath.replaceAll("^[/\\\\]+", ""); 6249 6250 acquireSingleUserCaseWriteLock(); 6251 TimelineManager timelineManager = getTimelineManager(); 6252 6253 CaseDbTransaction transaction = beginTransaction(); 6254 CaseDbConnection connection = transaction.getConnection(); 6255 try { 6256 final long parentId = parentObj.getId(); 6257 String parentPath = ""; 6258 if (parentObj instanceof BlackboardArtifact) { 6259 parentPath = parentObj.getUniquePath() + '/' + parentObj.getName() + '/'; 6260 } else if (parentObj instanceof AbstractFile) { 6261 parentPath = ((AbstractFile) parentObj).getParentPath() + parentObj.getName() + '/'; //NON-NLS 6262 } 6263 6264 // Insert a row for the derived file into the tsk_objects table. 6265 // INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?) 6266 long newObjId = addObject(parentId, TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection); 6267 6268 // Insert a row for the virtual directory into the tsk_files table. 6269 // INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type, 6270 // dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, known, mime_type, 6271 // parent_path, data_source_obj_id, extension) 6272 // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?) 6273 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE); 6274 statement.clearParameters(); 6275 statement.setLong(1, newObjId); 6276 6277 // If the parentFile is part of a file system, use its file system object ID. 6278 long fsObjId = this.getFileSystemId(parentId, connection); 6279 if (fsObjId != -1) { 6280 statement.setLong(2, fsObjId); 6281 } else { 6282 statement.setNull(2, java.sql.Types.BIGINT); 6283 } 6284 statement.setString(3, fileName); 6285 6286 //type, has_path 6287 statement.setShort(4, TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.getFileType()); 6288 statement.setShort(5, (short) 1); 6289 6290 //flags 6291 final TSK_FS_NAME_TYPE_ENUM dirType = isFile ? TSK_FS_NAME_TYPE_ENUM.REG : TSK_FS_NAME_TYPE_ENUM.DIR; 6292 statement.setShort(6, dirType.getValue()); 6293 final TSK_FS_META_TYPE_ENUM metaType = isFile ? TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG : TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR; 6294 statement.setShort(7, metaType.getValue()); 6295 6296 //note: using alloc under assumption that derived files derive from alloc files 6297 final TSK_FS_NAME_FLAG_ENUM dirFlag = TSK_FS_NAME_FLAG_ENUM.ALLOC; 6298 statement.setShort(8, dirFlag.getValue()); 6299 final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue() 6300 | TSK_FS_META_FLAG_ENUM.USED.getValue()); 6301 statement.setShort(9, metaFlags); 6302 6303 //size 6304 //prevent negative size 6305 long savedSize = size < 0 ? 0 : size; 6306 statement.setLong(10, savedSize); 6307 6308 //mactimes 6309 //long ctime, long crtime, long atime, long mtime, 6310 statement.setLong(11, ctime); 6311 statement.setLong(12, crtime); 6312 statement.setLong(13, atime); 6313 statement.setLong(14, mtime); 6314 6315 statement.setNull(15, java.sql.Types.VARCHAR); // MD5 6316 statement.setByte(16, FileKnown.UNKNOWN.getFileKnownValue()); // Known 6317 statement.setNull(17, java.sql.Types.VARCHAR); // MIME type 6318 6319 //parent path 6320 statement.setString(18, parentPath); 6321 6322 // root data source object id 6323 long dataSourceObjId = getDataSourceObjectId(connection, parentId); 6324 statement.setLong(19, dataSourceObjId); 6325 final String extension = extractExtension(fileName); 6326 //extension 6327 statement.setString(20, extension); 6328 6329 connection.executeUpdate(statement); 6330 6331 //add localPath 6332 addFilePath(connection, newObjId, localPath, encodingType); 6333 6334 DerivedFile derivedFile = new DerivedFile(this, newObjId, dataSourceObjId, fileName, dirType, metaType, dirFlag, metaFlags, 6335 savedSize, ctime, crtime, atime, mtime, null, null, parentPath, localPath, parentId, null, encodingType, extension); 6336 6337 timelineManager.addEventsForNewFile(derivedFile, connection); 6338 transaction.commit(); 6339 //TODO add derived method to tsk_files_derived and tsk_files_derived_method 6340 return derivedFile; 6341 } catch (SQLException ex) { 6342 connection.rollbackTransaction(); 6343 throw new TskCoreException("Failed to add derived file to case database", ex); 6344 } finally { 6345 connection.close(); 6346 releaseSingleUserCaseWriteLock(); 6347 } 6348 } 6349 6350 /** 6351 * Updates an existing derived file in the database and returns a new 6352 * derived file object with the updated contents 6353 * 6354 * @param derivedFile The derived file you wish to update 6355 * @param localPath local path of the derived file, including the file 6356 * name. The path is relative to the database path. 6357 * @param size size of the derived file in bytes 6358 * @param ctime The changed time of the file. 6359 * @param crtime The creation time of the file. 6360 * @param atime The accessed time of the file 6361 * @param mtime The modified time of the file. 6362 * @param isFile whether a file or directory, true if a file 6363 * @param mimeType The MIME type the updated file should have, null 6364 * to unset it 6365 * @param rederiveDetails details needed to re-derive file (will be specific 6366 * to the derivation method), currently unused 6367 * @param toolName name of derivation method/tool, currently unused 6368 * @param toolVersion version of derivation method/tool, currently 6369 * unused 6370 * @param otherDetails details of derivation method/tool, currently 6371 * unused 6372 * @param encodingType Type of encoding used on the file (or NONE if no 6373 * encoding) 6374 * 6375 * @return newly created derived file object which contains the updated data 6376 * 6377 * @throws TskCoreException exception thrown if the object creation failed 6378 * due to a critical system error 6379 */ 6380 public DerivedFile updateDerivedFile(DerivedFile derivedFile, String localPath, 6381 long size, long ctime, long crtime, long atime, long mtime, 6382 boolean isFile, String mimeType, 6383 String rederiveDetails, String toolName, String toolVersion, 6384 String otherDetails, TskData.EncodingType encodingType) throws TskCoreException { 6385 6386 // Strip off any leading slashes from the local path (leading slashes indicate absolute paths) 6387 localPath = localPath.replaceAll("^[/\\\\]+", ""); 6388 6389 CaseDbConnection connection = connections.getConnection(); 6390 acquireSingleUserCaseWriteLock(); 6391 ResultSet rs = null; 6392 try { 6393 Content parentObj = derivedFile.getParent(); 6394 connection.beginTransaction(); 6395 final long parentId = parentObj.getId(); 6396 String parentPath = ""; 6397 if (parentObj instanceof BlackboardArtifact) { 6398 parentPath = parentObj.getUniquePath() + '/' + parentObj.getName() + '/'; 6399 } else if (parentObj instanceof AbstractFile) { 6400 parentPath = ((AbstractFile) parentObj).getParentPath() + parentObj.getName() + '/'; //NON-NLS 6401 } 6402 // UPDATE tsk_files SET type = ?, dir_type = ?, meta_type = ?, dir_flags = ?, meta_flags = ?, " 6403 // + "size= ?, ctime= ?, crtime= ?, atime= ?, mtime= ?, mime_type = ? WHERE obj_id = ?"), //NON-NLS 6404 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.UPDATE_DERIVED_FILE); 6405 statement.clearParameters(); 6406 6407 //type 6408 statement.setShort(1, TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.getFileType()); 6409 6410 //flags 6411 final TSK_FS_NAME_TYPE_ENUM dirType = isFile ? TSK_FS_NAME_TYPE_ENUM.REG : TSK_FS_NAME_TYPE_ENUM.DIR; 6412 statement.setShort(2, dirType.getValue()); 6413 final TSK_FS_META_TYPE_ENUM metaType = isFile ? TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG : TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR; 6414 statement.setShort(3, metaType.getValue()); 6415 6416 //note: using alloc under assumption that derived files derive from alloc files 6417 final TSK_FS_NAME_FLAG_ENUM dirFlag = TSK_FS_NAME_FLAG_ENUM.ALLOC; 6418 statement.setShort(4, dirFlag.getValue()); 6419 final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue() 6420 | TSK_FS_META_FLAG_ENUM.USED.getValue()); 6421 statement.setShort(5, metaFlags); 6422 6423 //size 6424 //prevent negative size 6425 long savedSize = size < 0 ? 0 : size; 6426 statement.setLong(6, savedSize); 6427 6428 //mactimes 6429 //long ctime, long crtime, long atime, long mtime, 6430 statement.setLong(7, ctime); 6431 statement.setLong(8, crtime); 6432 statement.setLong(9, atime); 6433 statement.setLong(10, mtime); 6434 statement.setString(11, mimeType); 6435 statement.setString(12, String.valueOf(derivedFile.getId())); 6436 connection.executeUpdate(statement); 6437 6438 //add localPath 6439 updateFilePath(connection, derivedFile.getId(), localPath, encodingType); 6440 6441 connection.commitTransaction(); 6442 6443 long dataSourceObjId = getDataSourceObjectId(connection, parentId); 6444 final String extension = extractExtension(derivedFile.getName()); 6445 return new DerivedFile(this, derivedFile.getId(), dataSourceObjId, derivedFile.getName(), dirType, metaType, dirFlag, metaFlags, 6446 savedSize, ctime, crtime, atime, mtime, null, null, parentPath, localPath, parentId, null, encodingType, extension); 6447 } catch (SQLException ex) { 6448 connection.rollbackTransaction(); 6449 throw new TskCoreException("Failed to add derived file to case database", ex); 6450 } finally { 6451 closeResultSet(rs); 6452 connection.close(); 6453 releaseSingleUserCaseWriteLock(); 6454 } 6455 } 6456 6457 /** 6458 * Wraps the version of addLocalFile that takes a Transaction in a 6459 * transaction local to this method. 6460 * 6461 * @param fileName 6462 * @param localPath 6463 * @param size 6464 * @param ctime 6465 * @param crtime 6466 * @param atime 6467 * @param mtime 6468 * @param isFile 6469 * @param encodingType 6470 * @param parent 6471 * 6472 * @return 6473 * 6474 * @throws TskCoreException 6475 */ 6476 public LocalFile addLocalFile(String fileName, String localPath, 6477 long size, long ctime, long crtime, long atime, long mtime, 6478 boolean isFile, TskData.EncodingType encodingType, 6479 AbstractFile parent) throws TskCoreException { 6480 6481 CaseDbTransaction localTrans = beginTransaction(); 6482 try { 6483 LocalFile created = addLocalFile(fileName, localPath, size, ctime, crtime, atime, mtime, isFile, encodingType, parent, localTrans); 6484 localTrans.commit(); 6485 localTrans = null; 6486 return created; 6487 } finally { 6488 if (null != localTrans) { 6489 try { 6490 localTrans.rollback(); 6491 } catch (TskCoreException ex2) { 6492 logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex2); 6493 } 6494 } 6495 } 6496 } 6497 6498 /** 6499 * Adds a local/logical file to the case database. The database operations 6500 * are done within a caller-managed transaction; the caller is responsible 6501 * for committing or rolling back the transaction. 6502 * 6503 * @param fileName The name of the file. 6504 * @param localPath The absolute path (including the file name) of the 6505 * local/logical in secondary storage. 6506 * @param size The size of the file in bytes. 6507 * @param ctime The changed time of the file. 6508 * @param crtime The creation time of the file. 6509 * @param atime The accessed time of the file 6510 * @param mtime The modified time of the file. 6511 * @param isFile True, unless the file is a directory. 6512 * @param encodingType Type of encoding used on the file 6513 * @param parent The parent of the file (e.g., a virtual directory) 6514 * @param transaction A caller-managed transaction within which the add 6515 * file operations are performed. 6516 * 6517 * @return An object representing the local/logical file. 6518 * 6519 * @throws TskCoreException if there is an error completing a case database 6520 * operation. 6521 */ 6522 public LocalFile addLocalFile(String fileName, String localPath, 6523 long size, long ctime, long crtime, long atime, long mtime, 6524 boolean isFile, TskData.EncodingType encodingType, 6525 Content parent, CaseDbTransaction transaction) throws TskCoreException { 6526 6527 return addLocalFile(fileName, localPath, 6528 size, ctime, crtime, atime, mtime, 6529 null, null, null, 6530 isFile, encodingType, 6531 parent, transaction); 6532 } 6533 6534 /** 6535 * Adds a local/logical file to the case database. The database operations 6536 * are done within a caller-managed transaction; the caller is responsible 6537 * for committing or rolling back the transaction. 6538 * 6539 * @param fileName The name of the file. 6540 * @param localPath The absolute path (including the file name) of the 6541 * local/logical in secondary storage. 6542 * @param size The size of the file in bytes. 6543 * @param ctime The changed time of the file. 6544 * @param crtime The creation time of the file. 6545 * @param atime The accessed time of the file 6546 * @param mtime The modified time of the file. 6547 * @param md5 The MD5 hash of the file 6548 * @param known The known status of the file (can be null) 6549 * @param mimeType The MIME type of the file 6550 * @param isFile True, unless the file is a directory. 6551 * @param encodingType Type of encoding used on the file 6552 * @param parent The parent of the file (e.g., a virtual directory) 6553 * @param transaction A caller-managed transaction within which the add 6554 * file operations are performed. 6555 * 6556 * @return An object representing the local/logical file. 6557 * 6558 * @throws TskCoreException if there is an error completing a case database 6559 * operation. 6560 */ 6561 public LocalFile addLocalFile(String fileName, String localPath, 6562 long size, long ctime, long crtime, long atime, long mtime, 6563 String md5, FileKnown known, String mimeType, 6564 boolean isFile, TskData.EncodingType encodingType, 6565 Content parent, CaseDbTransaction transaction) throws TskCoreException { 6566 CaseDbConnection connection = transaction.getConnection(); 6567 transaction.acquireSingleUserCaseWriteLock(); 6568 Statement queryStatement = null; 6569 try { 6570 6571 // Insert a row for the local/logical file into the tsk_objects table. 6572 // INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?) 6573 long objectId = addObject(parent.getId(), TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection); 6574 6575 // Insert a row for the local/logical file into the tsk_files table. 6576 // INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type, 6577 // dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, known, mime_type, 6578 // parent_path, data_source_obj_id,extension) 6579 // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?) 6580 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE); 6581 statement.clearParameters(); 6582 statement.setLong(1, objectId); 6583 statement.setNull(2, java.sql.Types.BIGINT); // Not part of a file system 6584 statement.setString(3, fileName); 6585 statement.setShort(4, TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.getFileType()); 6586 statement.setShort(5, (short) 1); 6587 TSK_FS_NAME_TYPE_ENUM dirType = isFile ? TSK_FS_NAME_TYPE_ENUM.REG : TSK_FS_NAME_TYPE_ENUM.DIR; 6588 statement.setShort(6, dirType.getValue()); 6589 TSK_FS_META_TYPE_ENUM metaType = isFile ? TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG : TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR; 6590 statement.setShort(7, metaType.getValue()); 6591 TSK_FS_NAME_FLAG_ENUM dirFlag = TSK_FS_NAME_FLAG_ENUM.ALLOC; 6592 statement.setShort(8, dirFlag.getValue()); 6593 short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue() | TSK_FS_META_FLAG_ENUM.USED.getValue()); 6594 statement.setShort(9, metaFlags); 6595 //prevent negative size 6596 long savedSize = size < 0 ? 0 : size; 6597 statement.setLong(10, savedSize); 6598 statement.setLong(11, ctime); 6599 statement.setLong(12, crtime); 6600 statement.setLong(13, atime); 6601 statement.setLong(14, mtime); 6602 statement.setString(15, md5); 6603 if (known != null) { 6604 statement.setByte(16, known.getFileKnownValue()); 6605 } else { 6606 statement.setByte(16, FileKnown.UNKNOWN.getFileKnownValue()); 6607 } 6608 statement.setString(17, mimeType); 6609 String parentPath; 6610 long dataSourceObjId; 6611 6612 if (parent instanceof AbstractFile) { 6613 AbstractFile parentFile = (AbstractFile) parent; 6614 if (isRootDirectory(parentFile, transaction)) { 6615 parentPath = "/"; 6616 } else { 6617 parentPath = parentFile.getParentPath() + parent.getName() + "/"; //NON-NLS 6618 } 6619 dataSourceObjId = parentFile.getDataSourceObjectId(); 6620 } else { 6621 parentPath = "/"; 6622 dataSourceObjId = getDataSourceObjectId(connection, parent.getId()); 6623 } 6624 statement.setString(18, parentPath); 6625 statement.setLong(19, dataSourceObjId); 6626 final String extension = extractExtension(fileName); 6627 statement.setString(20, extension); 6628 6629 connection.executeUpdate(statement); 6630 addFilePath(connection, objectId, localPath, encodingType); 6631 LocalFile localFile = new LocalFile(this, 6632 objectId, 6633 fileName, 6634 TSK_DB_FILES_TYPE_ENUM.LOCAL, 6635 dirType, 6636 metaType, 6637 dirFlag, 6638 metaFlags, 6639 savedSize, 6640 ctime, crtime, atime, mtime, 6641 mimeType, md5, known, 6642 parent.getId(), parentPath, 6643 dataSourceObjId, 6644 localPath, 6645 encodingType, extension); 6646 getTimelineManager().addEventsForNewFile(localFile, connection); 6647 return localFile; 6648 6649 } catch (SQLException ex) { 6650 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); 6651 } finally { 6652 closeStatement(queryStatement); 6653 // NOTE: write lock will be released by transaction 6654 } 6655 } 6656 6657 /** 6658 * Check whether a given AbstractFile is the "root" directory. True if the 6659 * AbstractFile either has no parent or its parent is an image, volume, 6660 * volume system, or file system. 6661 * 6662 * @param file the file to test 6663 * @param transaction the current transaction 6664 * 6665 * @return true if the file is a root directory, false otherwise 6666 * 6667 * @throws TskCoreException 6668 */ 6669 private boolean isRootDirectory(AbstractFile file, CaseDbTransaction transaction) throws TskCoreException { 6670 CaseDbConnection connection = transaction.getConnection(); 6671 transaction.acquireSingleUserCaseWriteLock(); 6672 Statement statement = null; 6673 ResultSet resultSet = null; 6674 6675 try { 6676 String query = String.format("SELECT ParentRow.type AS parent_type, ParentRow.obj_id AS parent_object_id " 6677 + "FROM tsk_objects ParentRow JOIN tsk_objects ChildRow ON ChildRow.par_obj_id = ParentRow.obj_id " 6678 + "WHERE ChildRow.obj_id = %s;", file.getId()); 6679 6680 statement = connection.createStatement(); 6681 resultSet = statement.executeQuery(query); 6682 if (resultSet.next()) { 6683 long parentId = resultSet.getLong("parent_object_id"); 6684 if (parentId == 0) { 6685 return true; 6686 } 6687 int type = resultSet.getInt("parent_type"); 6688 return (type == TskData.ObjectType.IMG.getObjectType() 6689 || type == TskData.ObjectType.VS.getObjectType() 6690 || type == TskData.ObjectType.VOL.getObjectType() 6691 || type == TskData.ObjectType.FS.getObjectType()); 6692 6693 } else { 6694 return true; // The file has no parent 6695 } 6696 } catch (SQLException ex) { 6697 throw new TskCoreException(String.format("Failed to lookup parent of file (%s) with id %d", file.getName(), file.getId()), ex); 6698 } finally { 6699 closeResultSet(resultSet); 6700 closeStatement(statement); 6701 // NOTE: write lock will be released by transaction 6702 } 6703 } 6704 6705 /** 6706 * Add a new layout file to the database. 6707 * 6708 * @param fileName The name of the file. 6709 * @param size The size of the file in bytes. 6710 * @param dirFlag The allocated status from the name structure 6711 * @param metaFlag The allocated status from the metadata structure 6712 * @param ctime The changed time of the file. 6713 * @param crtime The creation time of the file. 6714 * @param atime The accessed time of the file 6715 * @param mtime The modified time of the file. 6716 * @param fileRanges The byte ranges that belong to this file (relative to 6717 * start of image) 6718 * @param parent The parent of the file 6719 * 6720 * @return The new LayoutFile 6721 * 6722 * @throws TskCoreException 6723 */ 6724 public LayoutFile addLayoutFile(String fileName, 6725 long size, 6726 TSK_FS_NAME_FLAG_ENUM dirFlag, TSK_FS_META_FLAG_ENUM metaFlag, 6727 long ctime, long crtime, long atime, long mtime, 6728 List<TskFileRange> fileRanges, 6729 Content parent) throws TskCoreException { 6730 6731 if (null == parent) { 6732 throw new TskCoreException("Parent can not be null"); 6733 } 6734 6735 String parentPath; 6736 if (parent instanceof AbstractFile) { 6737 parentPath = ((AbstractFile) parent).getParentPath() + parent.getName() + '/'; //NON-NLS 6738 } else { 6739 parentPath = "/"; 6740 } 6741 6742 CaseDbTransaction transaction = null; 6743 Statement statement = null; 6744 ResultSet resultSet = null; 6745 try { 6746 transaction = beginTransaction(); 6747 transaction.acquireSingleUserCaseWriteLock(); 6748 CaseDbConnection connection = transaction.getConnection(); 6749 6750 /* 6751 * Insert a row for the layout file into the tsk_objects table: 6752 * INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?) 6753 */ 6754 long newFileId = addObject(parent.getId(), TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection); 6755 6756 /* 6757 * Insert a row for the file into the tsk_files table: INSERT INTO 6758 * tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, 6759 * meta_type, dir_flags, meta_flags, size, ctime, crtime, atime, 6760 * mtime, md5, known, mime_type, parent_path, 6761 * data_source_obj_id,extenion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 6762 * ?, ?, ?, ?, ?, ?, ?,?) 6763 */ 6764 PreparedStatement prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE); 6765 prepStmt.clearParameters(); 6766 prepStmt.setLong(1, newFileId); // obj_id 6767 6768 // If the parent is part of a file system, grab its file system ID 6769 if (0 != parent.getId()) { 6770 long parentFs = this.getFileSystemId(parent.getId(), connection); 6771 if (parentFs != -1) { 6772 prepStmt.setLong(2, parentFs); 6773 } else { 6774 prepStmt.setNull(2, java.sql.Types.BIGINT); 6775 } 6776 } else { 6777 prepStmt.setNull(2, java.sql.Types.BIGINT); 6778 } 6779 prepStmt.setString(3, fileName); // name 6780 prepStmt.setShort(4, TSK_DB_FILES_TYPE_ENUM.LAYOUT_FILE.getFileType()); // type 6781 prepStmt.setShort(5, (short) 0); // has_path 6782 prepStmt.setShort(6, TSK_FS_NAME_TYPE_ENUM.REG.getValue()); // dir_type 6783 prepStmt.setShort(7, TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue()); // meta_type 6784 prepStmt.setShort(8, dirFlag.getValue()); // dir_flags 6785 prepStmt.setShort(9, metaFlag.getValue()); // meta_flags 6786 //prevent negative size 6787 long savedSize = size < 0 ? 0 : size; 6788 prepStmt.setLong(10, savedSize); // size 6789 prepStmt.setLong(11, ctime); // ctime 6790 prepStmt.setLong(12, crtime); // crtime 6791 prepStmt.setLong(13, atime); // atime 6792 prepStmt.setLong(14, mtime); // mtime 6793 prepStmt.setNull(15, java.sql.Types.VARCHAR); // MD5 6794 prepStmt.setByte(16, FileKnown.UNKNOWN.getFileKnownValue()); // Known 6795 prepStmt.setNull(17, java.sql.Types.VARCHAR); // MIME type 6796 prepStmt.setString(18, parentPath); // parent path 6797 prepStmt.setLong(19, parent.getDataSource().getId()); // data_source_obj_id 6798 6799 prepStmt.setString(20, extractExtension(fileName)); //extension 6800 connection.executeUpdate(prepStmt); 6801 6802 /* 6803 * Insert a row in the tsk_layout_file table for each chunk of the 6804 * carved file. INSERT INTO tsk_file_layout (obj_id, byte_start, 6805 * byte_len, sequence) VALUES (?, ?, ?, ?) 6806 */ 6807 prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LAYOUT_FILE); 6808 for (TskFileRange tskFileRange : fileRanges) { 6809 prepStmt.clearParameters(); 6810 prepStmt.setLong(1, newFileId); // obj_id 6811 prepStmt.setLong(2, tskFileRange.getByteStart()); // byte_start 6812 prepStmt.setLong(3, tskFileRange.getByteLen()); // byte_len 6813 prepStmt.setLong(4, tskFileRange.getSequence()); // sequence 6814 connection.executeUpdate(prepStmt); 6815 } 6816 6817 /* 6818 * Create a layout file representation of the carved file. 6819 */ 6820 LayoutFile layoutFile = new LayoutFile(this, 6821 newFileId, 6822 parent.getDataSource().getId(), 6823 fileName, 6824 TSK_DB_FILES_TYPE_ENUM.LAYOUT_FILE, 6825 TSK_FS_NAME_TYPE_ENUM.REG, 6826 TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG, 6827 dirFlag, 6828 metaFlag.getValue(), 6829 savedSize, 6830 ctime, crtime, atime, mtime, 6831 null, 6832 FileKnown.UNKNOWN, 6833 parentPath, 6834 null); 6835 6836 transaction.commit(); 6837 transaction = null; 6838 return layoutFile; 6839 6840 } catch (SQLException ex) { 6841 throw new TskCoreException("Failed to add layout file " + fileName + " to case database", ex); 6842 } finally { 6843 closeResultSet(resultSet); 6844 closeStatement(statement); 6845 6846 // NOTE: write lock will be released by transaction 6847 if (null != transaction) { 6848 try { 6849 transaction.rollback(); 6850 } catch (TskCoreException ex2) { 6851 logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex2); 6852 } 6853 } 6854 } 6855 } 6856 6857 /** 6858 * Given an object id, works up the tree of ancestors to the data source for 6859 * the object and gets the object id of the data source. The trivial case 6860 * where the input object id is for a source is handled. 6861 * 6862 * @param connection A case database connection. 6863 * @param objectId An object id. 6864 * 6865 * @return A data source object id. 6866 * 6867 * @throws TskCoreException if there is an error querying the case database. 6868 */ 6869 private long getDataSourceObjectId(CaseDbConnection connection, long objectId) throws TskCoreException { 6870 acquireSingleUserCaseReadLock(); 6871 Statement statement = null; 6872 ResultSet resultSet = null; 6873 try { 6874 statement = connection.createStatement(); 6875 long dataSourceObjId; 6876 long ancestorId = objectId; 6877 do { 6878 dataSourceObjId = ancestorId; 6879 String query = String.format("SELECT par_obj_id FROM tsk_objects WHERE obj_id = %s;", ancestorId); 6880 resultSet = statement.executeQuery(query); 6881 if (resultSet.next()) { 6882 ancestorId = resultSet.getLong("par_obj_id"); 6883 } else { 6884 throw new TskCoreException(String.format("tsk_objects table is corrupt, SQL query returned no result: %s", query)); 6885 } 6886 resultSet.close(); 6887 resultSet = null; 6888 } while (0 != ancestorId); // Not NULL 6889 return dataSourceObjId; 6890 } catch (SQLException ex) { 6891 throw new TskCoreException(String.format("Error finding root data source for object (obj_id = %d)", objectId), ex); 6892 } finally { 6893 closeResultSet(resultSet); 6894 closeStatement(statement); 6895 releaseSingleUserCaseReadLock(); 6896 } 6897 } 6898 6899 /** 6900 * Add a path (such as a local path) for a content object to tsk_file_paths 6901 * 6902 * @param connection A case database connection. 6903 * @param objId The object id of the file for which to add the path. 6904 * @param path The path to add. 6905 * @param type The TSK encoding type of the file. 6906 * 6907 * @throws SQLException Thrown if database error occurred and path was not 6908 * added. 6909 */ 6910 private void addFilePath(CaseDbConnection connection, long objId, String path, TskData.EncodingType type) throws SQLException { 6911 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LOCAL_PATH); 6912 statement.clearParameters(); 6913 statement.setLong(1, objId); 6914 statement.setString(2, path); 6915 statement.setInt(3, type.getType()); 6916 connection.executeUpdate(statement); 6917 } 6918 6919 /** 6920 * Update the path for a content object in the tsk_file_paths table 6921 * 6922 * @param connection A case database connection. 6923 * @param objId The object id of the file for which to update the path. 6924 * @param path The path to update. 6925 * @param type The TSK encoding type of the file. 6926 * 6927 * @throws SQLException Thrown if database error occurred and path was not 6928 * updated. 6929 */ 6930 private void updateFilePath(CaseDbConnection connection, long objId, String path, TskData.EncodingType type) throws SQLException { 6931 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.UPDATE_LOCAL_PATH); 6932 statement.clearParameters(); 6933 statement.setString(1, path); 6934 statement.setInt(2, type.getType()); 6935 statement.setLong(3, objId); 6936 connection.executeUpdate(statement); 6937 } 6938 6939 /** 6940 * Find all files in the data source, by name and parent 6941 * 6942 * @param dataSource the dataSource (Image, parent-less VirtualDirectory) to 6943 * search for the given file name 6944 * @param fileName Pattern of the name of the file or directory to match 6945 * (case insensitive, used in LIKE SQL statement). 6946 * @param parentFile Object for parent file/directory to find children in 6947 * 6948 * @return a list of AbstractFile for files/directories whose name matches 6949 * fileName and that were inside a directory described by 6950 * parentFile. 6951 * 6952 * @throws org.sleuthkit.datamodel.TskCoreException 6953 */ 6954 public List<AbstractFile> findFiles(Content dataSource, String fileName, AbstractFile parentFile) throws TskCoreException { 6955 return findFiles(dataSource, fileName, parentFile.getName()); 6956 } 6957 6958 /** 6959 * Count files matching the specific Where clause 6960 * 6961 * @param sqlWhereClause a SQL where clause appropriate for the desired 6962 * files (do not begin the WHERE clause with the word 6963 * WHERE!) 6964 * 6965 * @return count of files each of which satisfy the given WHERE clause 6966 * 6967 * @throws TskCoreException \ref query_database_page 6968 */ 6969 public long countFilesWhere(String sqlWhereClause) throws TskCoreException { 6970 CaseDbConnection connection = connections.getConnection(); 6971 acquireSingleUserCaseReadLock(); 6972 Statement s = null; 6973 ResultSet rs = null; 6974 try { 6975 s = connection.createStatement(); 6976 rs = connection.executeQuery(s, "SELECT COUNT(*) AS count FROM tsk_files WHERE " + sqlWhereClause); //NON-NLS 6977 rs.next(); 6978 return rs.getLong("count"); 6979 } catch (SQLException e) { 6980 throw new TskCoreException("SQLException thrown when calling 'SleuthkitCase.countFilesWhere().", e); 6981 } finally { 6982 closeResultSet(rs); 6983 closeStatement(s); 6984 connection.close(); 6985 releaseSingleUserCaseReadLock(); 6986 } 6987 } 6988 6989 /** 6990 * Find and return list of all (abstract) files matching the specific Where 6991 * clause. You need to know the database schema to use this, which is 6992 * outlined on the 6993 * <a href="http://wiki.sleuthkit.org/index.php?title=SQLite_Database_v3_Schema">wiki</a>. 6994 * You should use enums from org.sleuthkit.datamodel.TskData to make the 6995 * queries easier to maintain and understand. 6996 * 6997 * @param sqlWhereClause a SQL where clause appropriate for the desired 6998 * files (do not begin the WHERE clause with the word 6999 * WHERE!) 7000 * 7001 * @return a list of AbstractFile each of which satisfy the given WHERE 7002 * clause 7003 * 7004 * @throws TskCoreException \ref query_database_page 7005 */ 7006 public List<AbstractFile> findAllFilesWhere(String sqlWhereClause) throws TskCoreException { 7007 CaseDbConnection connection = connections.getConnection(); 7008 acquireSingleUserCaseReadLock(); 7009 Statement s = null; 7010 ResultSet rs = null; 7011 try { 7012 s = connection.createStatement(); 7013 rs = connection.executeQuery(s, "SELECT * FROM tsk_files WHERE " + sqlWhereClause); //NON-NLS 7014 return resultSetToAbstractFiles(rs, connection); 7015 } catch (SQLException e) { 7016 throw new TskCoreException("SQLException thrown when calling 'SleuthkitCase.findAllFilesWhere(): " + sqlWhereClause, e); 7017 } finally { 7018 closeResultSet(rs); 7019 closeStatement(s); 7020 connection.close(); 7021 releaseSingleUserCaseReadLock(); 7022 } 7023 } 7024 7025 /** 7026 * Find and return list of all (abstract) ids of files matching the specific 7027 * Where clause 7028 * 7029 * @param sqlWhereClause a SQL where clause appropriate for the desired 7030 * files (do not begin the WHERE clause with the word 7031 * WHERE!) 7032 * 7033 * @return a list of file ids each of which satisfy the given WHERE clause 7034 * 7035 * @throws TskCoreException \ref query_database_page 7036 */ 7037 public List<Long> findAllFileIdsWhere(String sqlWhereClause) throws TskCoreException { 7038 CaseDbConnection connection = connections.getConnection(); 7039 acquireSingleUserCaseReadLock(); 7040 Statement s = null; 7041 ResultSet rs = null; 7042 try { 7043 s = connection.createStatement(); 7044 rs = connection.executeQuery(s, "SELECT obj_id FROM tsk_files WHERE " + sqlWhereClause); //NON-NLS 7045 List<Long> ret = new ArrayList<Long>(); 7046 while (rs.next()) { 7047 ret.add(rs.getLong("obj_id")); 7048 } 7049 return ret; 7050 } catch (SQLException e) { 7051 throw new TskCoreException("SQLException thrown when calling 'SleuthkitCase.findAllFileIdsWhere(): " + sqlWhereClause, e); 7052 } finally { 7053 closeResultSet(rs); 7054 closeStatement(s); 7055 connection.close(); 7056 releaseSingleUserCaseReadLock(); 7057 } 7058 } 7059 7060 /** 7061 * @param dataSource the data source (Image, VirtualDirectory for file-sets, 7062 * etc) to search for the given file name 7063 * @param filePath The full path to the file(s) of interest. This can 7064 * optionally include the image and volume names. Treated 7065 * in a case- insensitive manner. 7066 * 7067 * @return a list of AbstractFile that have the given file path. 7068 * 7069 * @throws org.sleuthkit.datamodel.TskCoreException 7070 */ 7071 public List<AbstractFile> openFiles(Content dataSource, String filePath) throws TskCoreException { 7072 7073 // get the non-unique path (strip of image and volume path segments, if 7074 // the exist. 7075 String path = AbstractFile.createNonUniquePath(filePath).toLowerCase(); 7076 7077 // split the file name from the parent path 7078 int lastSlash = path.lastIndexOf('/'); //NON-NLS 7079 7080 // if the last slash is at the end, strip it off 7081 if (lastSlash == path.length()) { 7082 path = path.substring(0, lastSlash - 1); 7083 lastSlash = path.lastIndexOf('/'); //NON-NLS 7084 } 7085 7086 String parentPath = path.substring(0, lastSlash); 7087 String fileName = path.substring(lastSlash); 7088 7089 return findFiles(dataSource, fileName, parentPath); 7090 } 7091 7092 /** 7093 * Get file layout ranges from tsk_file_layout, for a file with specified id 7094 * 7095 * @param id of the file to get file layout ranges for 7096 * 7097 * @return list of populated file ranges 7098 * 7099 * @throws TskCoreException thrown if a critical error occurred within tsk 7100 * core 7101 */ 7102 public List<TskFileRange> getFileRanges(long id) throws TskCoreException { 7103 CaseDbConnection connection = connections.getConnection(); 7104 acquireSingleUserCaseReadLock(); 7105 Statement s = null; 7106 ResultSet rs = null; 7107 try { 7108 s = connection.createStatement(); 7109 rs = connection.executeQuery(s, "SELECT * FROM tsk_file_layout WHERE obj_id = " + id + " ORDER BY sequence"); 7110 List<TskFileRange> ranges = new ArrayList<TskFileRange>(); 7111 while (rs.next()) { 7112 TskFileRange range = new TskFileRange(rs.getLong("byte_start"), //NON-NLS 7113 rs.getLong("byte_len"), rs.getLong("sequence")); //NON-NLS 7114 ranges.add(range); 7115 } 7116 return ranges; 7117 } catch (SQLException ex) { 7118 throw new TskCoreException("Error getting TskFileLayoutRanges by id, id = " + id, ex); 7119 } finally { 7120 closeResultSet(rs); 7121 closeStatement(s); 7122 connection.close(); 7123 releaseSingleUserCaseReadLock(); 7124 } 7125 } 7126 7127 /** 7128 * Get am image by the image object id 7129 * 7130 * @param id of the image object 7131 * 7132 * @return Image object populated 7133 * 7134 * @throws TskCoreException thrown if a critical error occurred within tsk 7135 * core 7136 */ 7137 public Image getImageById(long id) throws TskCoreException { 7138 CaseDbConnection connection = connections.getConnection(); 7139 acquireSingleUserCaseReadLock(); 7140 Statement s1 = null; 7141 ResultSet rs1 = null; 7142 Statement s2 = null; 7143 ResultSet rs2 = null; 7144 try { 7145 s1 = connection.createStatement(); 7146 rs1 = connection.executeQuery(s1, "SELECT tsk_image_info.type, tsk_image_info.ssize, tsk_image_info.tzone, tsk_image_info.size, tsk_image_info.md5, tsk_image_info.sha1, tsk_image_info.sha256, tsk_image_info.display_name, data_source_info.device_id " 7147 + "FROM tsk_image_info " 7148 + "INNER JOIN data_source_info ON tsk_image_info.obj_id = data_source_info.obj_id " 7149 + "WHERE tsk_image_info.obj_id = " + id); //NON-NLS 7150 if (rs1.next()) { 7151 s2 = connection.createStatement(); 7152 rs2 = connection.executeQuery(s2, "SELECT name FROM tsk_image_names WHERE tsk_image_names.obj_id = " + id); //NON-NLS 7153 List<String> imagePaths = new ArrayList<String>(); 7154 while (rs2.next()) { 7155 imagePaths.add(rs2.getString("name")); 7156 } 7157 long type = rs1.getLong("type"); //NON-NLS 7158 long ssize = rs1.getLong("ssize"); //NON-NLS 7159 String tzone = rs1.getString("tzone"); //NON-NLS 7160 long size = rs1.getLong("size"); //NON-NLS 7161 String md5 = rs1.getString("md5"); //NON-NLS 7162 String sha1 = rs1.getString("sha1"); //NON-NLS 7163 String sha256 = rs1.getString("sha256"); //NON-NLS 7164 String name = rs1.getString("display_name"); 7165 if (name == null) { 7166 if (imagePaths.size() > 0) { 7167 String path = imagePaths.get(0); 7168 name = (new java.io.File(path)).getName(); 7169 } else { 7170 name = ""; 7171 } 7172 } 7173 String device_id = rs1.getString("device_id"); 7174 7175 return new Image(this, id, type, device_id, ssize, name, 7176 imagePaths.toArray(new String[imagePaths.size()]), tzone, md5, sha1, sha256, size); 7177 } else { 7178 throw new TskCoreException("No image found for id: " + id); 7179 } 7180 } catch (SQLException ex) { 7181 throw new TskCoreException("Error getting Image by id, id = " + id, ex); 7182 } finally { 7183 closeResultSet(rs2); 7184 closeStatement(s2); 7185 closeResultSet(rs1); 7186 closeStatement(s1); 7187 connection.close(); 7188 releaseSingleUserCaseReadLock(); 7189 } 7190 } 7191 7192 /** 7193 * Get a volume system by the volume system object id 7194 * 7195 * @param id id of the volume system 7196 * @param parent image containing the volume system 7197 * 7198 * @return populated VolumeSystem object 7199 * 7200 * @throws TskCoreException thrown if a critical error occurred within tsk 7201 * core 7202 */ 7203 VolumeSystem getVolumeSystemById(long id, Image parent) throws TskCoreException { 7204 CaseDbConnection connection = connections.getConnection(); 7205 acquireSingleUserCaseReadLock(); 7206 Statement s = null; 7207 ResultSet rs = null; 7208 try { 7209 s = connection.createStatement(); 7210 rs = connection.executeQuery(s, "SELECT * FROM tsk_vs_info " //NON-NLS 7211 + "where obj_id = " + id); //NON-NLS 7212 if (rs.next()) { 7213 long type = rs.getLong("vs_type"); //NON-NLS 7214 long imgOffset = rs.getLong("img_offset"); //NON-NLS 7215 long blockSize = rs.getLong("block_size"); //NON-NLS 7216 VolumeSystem vs = new VolumeSystem(this, id, "", type, imgOffset, blockSize); 7217 vs.setParent(parent); 7218 return vs; 7219 } else { 7220 throw new TskCoreException("No volume system found for id:" + id); 7221 } 7222 } catch (SQLException ex) { 7223 throw new TskCoreException("Error getting Volume System by ID.", ex); 7224 } finally { 7225 closeResultSet(rs); 7226 closeStatement(s); 7227 connection.close(); 7228 releaseSingleUserCaseReadLock(); 7229 } 7230 } 7231 7232 /** 7233 * @param id ID of the desired VolumeSystem 7234 * @param parentId ID of the VolumeSystem's parent 7235 * 7236 * @return the VolumeSystem with the given ID 7237 * 7238 * @throws TskCoreException 7239 */ 7240 VolumeSystem getVolumeSystemById(long id, long parentId) throws TskCoreException { 7241 VolumeSystem vs = getVolumeSystemById(id, null); 7242 vs.setParentId(parentId); 7243 return vs; 7244 } 7245 7246 /** 7247 * Get a file system by the object id 7248 * 7249 * @param id of the filesystem 7250 * @param parent parent Image of the file system 7251 * 7252 * @return populated FileSystem object 7253 * 7254 * @throws TskCoreException thrown if a critical error occurred within tsk 7255 * core 7256 */ 7257 FileSystem getFileSystemById(long id, Image parent) throws TskCoreException { 7258 return getFileSystemByIdHelper(id, parent); 7259 } 7260 7261 /** 7262 * @param id ID of the desired FileSystem 7263 * @param parentId ID of the FileSystem's parent 7264 * 7265 * @return the desired FileSystem 7266 * 7267 * @throws TskCoreException 7268 */ 7269 FileSystem getFileSystemById(long id, long parentId) throws TskCoreException { 7270 Volume vol = null; 7271 FileSystem fs = getFileSystemById(id, vol); 7272 fs.setParentId(parentId); 7273 return fs; 7274 } 7275 7276 /** 7277 * Get a file system by the object id 7278 * 7279 * @param id of the filesystem 7280 * @param parent parent Volume of the file system 7281 * 7282 * @return populated FileSystem object 7283 * 7284 * @throws TskCoreException thrown if a critical error occurred within tsk 7285 * core 7286 */ 7287 FileSystem getFileSystemById(long id, Volume parent) throws TskCoreException { 7288 return getFileSystemByIdHelper(id, parent); 7289 } 7290 7291 /** 7292 * Get file system by id and Content parent 7293 * 7294 * @param id of the filesystem to get 7295 * @param parent a direct parent Content object 7296 * 7297 * @return populated FileSystem object 7298 * 7299 * @throws TskCoreException thrown if a critical error occurred within tsk 7300 * core 7301 */ 7302 private FileSystem getFileSystemByIdHelper(long id, Content parent) throws TskCoreException { 7303 // see if we already have it 7304 // @@@ NOTE: this is currently kind of bad in that we are ignoring the parent value, 7305 // but it should be the same... 7306 synchronized (fileSystemIdMap) { 7307 if (fileSystemIdMap.containsKey(id)) { 7308 return fileSystemIdMap.get(id); 7309 } 7310 } 7311 CaseDbConnection connection = connections.getConnection(); 7312 acquireSingleUserCaseReadLock(); 7313 Statement s = null; 7314 ResultSet rs = null; 7315 try { 7316 s = connection.createStatement(); 7317 rs = connection.executeQuery(s, "SELECT * FROM tsk_fs_info " //NON-NLS 7318 + "where obj_id = " + id); //NON-NLS 7319 if (rs.next()) { 7320 TskData.TSK_FS_TYPE_ENUM fsType = TskData.TSK_FS_TYPE_ENUM.valueOf(rs.getInt("fs_type")); //NON-NLS 7321 FileSystem fs = new FileSystem(this, rs.getLong("obj_id"), "", rs.getLong("img_offset"), //NON-NLS 7322 fsType, rs.getLong("block_size"), rs.getLong("block_count"), //NON-NLS 7323 rs.getLong("root_inum"), rs.getLong("first_inum"), rs.getLong("last_inum")); //NON-NLS 7324 fs.setParent(parent); 7325 // save it for the next call 7326 synchronized (fileSystemIdMap) { 7327 fileSystemIdMap.put(id, fs); 7328 } 7329 return fs; 7330 } else { 7331 throw new TskCoreException("No file system found for id:" + id); 7332 } 7333 } catch (SQLException ex) { 7334 throw new TskCoreException("Error getting File System by ID", ex); 7335 } finally { 7336 closeResultSet(rs); 7337 closeStatement(s); 7338 connection.close(); 7339 releaseSingleUserCaseReadLock(); 7340 } 7341 } 7342 7343 /** 7344 * Get volume by id 7345 * 7346 * @param id 7347 * @param parent volume system 7348 * 7349 * @return populated Volume object 7350 * 7351 * @throws TskCoreException thrown if a critical error occurred within tsk 7352 * core 7353 */ 7354 Volume getVolumeById(long id, VolumeSystem parent) throws TskCoreException { 7355 CaseDbConnection connection = connections.getConnection(); 7356 acquireSingleUserCaseReadLock(); 7357 Statement s = null; 7358 ResultSet rs = null; 7359 try { 7360 s = connection.createStatement(); 7361 rs = connection.executeQuery(s, "SELECT * FROM tsk_vs_parts " //NON-NLS 7362 + "where obj_id = " + id); //NON-NLS 7363 if (rs.next()) { 7364 /** 7365 * TODO!! LANDMINE!! This allows the two types of databases to 7366 * have slightly different schemas. SQLite uses desc as the 7367 * column name in tsk_vs_parts and Postgres uses descr, as desc 7368 * is a reserved keyword in Postgres. When we have to make a 7369 * schema change, be sure to change this over to just one name. 7370 */ 7371 String description; 7372 try { 7373 description = rs.getString("desc"); 7374 } catch (Exception ex) { 7375 description = rs.getString("descr"); 7376 } 7377 Volume vol = new Volume(this, rs.getLong("obj_id"), rs.getLong("addr"), //NON-NLS 7378 rs.getLong("start"), rs.getLong("length"), rs.getLong("flags"), //NON-NLS 7379 description); 7380 vol.setParent(parent); 7381 return vol; 7382 } else { 7383 throw new TskCoreException("No volume found for id:" + id); 7384 } 7385 } catch (SQLException ex) { 7386 throw new TskCoreException("Error getting Volume by ID", ex); 7387 } finally { 7388 closeResultSet(rs); 7389 closeStatement(s); 7390 connection.close(); 7391 releaseSingleUserCaseReadLock(); 7392 } 7393 } 7394 7395 /** 7396 * @param id ID of the desired Volume 7397 * @param parentId ID of the Volume's parent 7398 * 7399 * @return the desired Volume 7400 * 7401 * @throws TskCoreException 7402 */ 7403 Volume getVolumeById(long id, long parentId) throws TskCoreException { 7404 Volume vol = getVolumeById(id, null); 7405 vol.setParentId(parentId); 7406 return vol; 7407 } 7408 7409 /** 7410 * Get a directory by id 7411 * 7412 * @param id of the directory object 7413 * @param parentFs parent file system 7414 * 7415 * @return populated Directory object 7416 * 7417 * @throws TskCoreException thrown if a critical error occurred within tsk 7418 * core 7419 */ 7420 Directory getDirectoryById(long id, FileSystem parentFs) throws TskCoreException { 7421 CaseDbConnection connection = connections.getConnection(); 7422 acquireSingleUserCaseReadLock(); 7423 Statement s = null; 7424 ResultSet rs = null; 7425 try { 7426 s = connection.createStatement(); 7427 rs = connection.executeQuery(s, "SELECT * FROM tsk_files " //NON-NLS 7428 + "WHERE obj_id = " + id); 7429 Directory temp = null; //NON-NLS 7430 if (rs.next()) { 7431 final short type = rs.getShort("type"); //NON-NLS 7432 if (type == TSK_DB_FILES_TYPE_ENUM.FS.getFileType()) { 7433 if (rs.getShort("meta_type") == TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue() 7434 || rs.getShort("meta_type") == TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT_DIR.getValue()) { //NON-NLS 7435 temp = directory(rs, parentFs); 7436 } 7437 } else if (type == TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()) { 7438 throw new TskCoreException("Expecting an FS-type directory, got virtual, id: " + id); 7439 } 7440 } else { 7441 throw new TskCoreException("No Directory found for id:" + id); 7442 } 7443 return temp; 7444 } catch (SQLException ex) { 7445 throw new TskCoreException("Error getting Directory by ID", ex); 7446 } finally { 7447 closeResultSet(rs); 7448 closeStatement(s); 7449 connection.close(); 7450 releaseSingleUserCaseReadLock(); 7451 } 7452 } 7453 7454 /** 7455 * Helper to return FileSystems in an Image 7456 * 7457 * @param image Image to lookup FileSystem for 7458 * 7459 * @return Collection of FileSystems in the image 7460 */ 7461 public Collection<FileSystem> getFileSystems(Image image) { 7462 List<FileSystem> fileSystems = new ArrayList<FileSystem>(); 7463 CaseDbConnection connection; 7464 try { 7465 connection = connections.getConnection(); 7466 } catch (TskCoreException ex) { 7467 logger.log(Level.SEVERE, "Error getting file systems for image " + image.getId(), ex); //NON-NLS 7468 return fileSystems; 7469 } 7470 acquireSingleUserCaseReadLock(); 7471 Statement s = null; 7472 ResultSet rs = null; 7473 try { 7474 s = connection.createStatement(); 7475 7476 // Get all the file systems. 7477 List<FileSystem> allFileSystems = new ArrayList<FileSystem>(); 7478 try { 7479 rs = connection.executeQuery(s, "SELECT * FROM tsk_fs_info"); //NON-NLS 7480 while (rs.next()) { 7481 TskData.TSK_FS_TYPE_ENUM fsType = TskData.TSK_FS_TYPE_ENUM.valueOf(rs.getInt("fs_type")); //NON-NLS 7482 FileSystem fs = new FileSystem(this, rs.getLong("obj_id"), "", rs.getLong("img_offset"), //NON-NLS 7483 fsType, rs.getLong("block_size"), rs.getLong("block_count"), //NON-NLS 7484 rs.getLong("root_inum"), rs.getLong("first_inum"), rs.getLong("last_inum")); //NON-NLS 7485 fs.setParent(null); 7486 allFileSystems.add(fs); 7487 } 7488 } catch (SQLException ex) { 7489 logger.log(Level.SEVERE, "There was a problem while trying to obtain all file systems", ex); //NON-NLS 7490 } finally { 7491 closeResultSet(rs); 7492 rs = null; 7493 } 7494 7495 // For each file system, find the image to which it belongs by iteratively 7496 // climbing the tsk_ojbects hierarchy only taking those file systems 7497 // that belong to this image. 7498 for (FileSystem fs : allFileSystems) { 7499 Long imageID = null; 7500 Long currentObjID = fs.getId(); 7501 while (imageID == null) { 7502 try { 7503 rs = connection.executeQuery(s, "SELECT * FROM tsk_objects WHERE tsk_objects.obj_id = " + currentObjID); //NON-NLS 7504 rs.next(); 7505 currentObjID = rs.getLong("par_obj_id"); //NON-NLS 7506 if (rs.getInt("type") == TskData.ObjectType.IMG.getObjectType()) { //NON-NLS 7507 imageID = rs.getLong("obj_id"); //NON-NLS 7508 } 7509 } catch (SQLException ex) { 7510 logger.log(Level.SEVERE, "There was a problem while trying to obtain this image's file systems", ex); //NON-NLS 7511 } finally { 7512 closeResultSet(rs); 7513 rs = null; 7514 } 7515 } 7516 7517 // see if imageID is this image's ID 7518 if (imageID == image.getId()) { 7519 fileSystems.add(fs); 7520 } 7521 } 7522 } catch (SQLException ex) { 7523 logger.log(Level.SEVERE, "Error getting case database connection", ex); //NON-NLS 7524 } finally { 7525 closeResultSet(rs); 7526 closeStatement(s); 7527 connection.close(); 7528 releaseSingleUserCaseReadLock(); 7529 } 7530 return fileSystems; 7531 } 7532 7533 /** 7534 * Returns the list of direct children for a given Image 7535 * 7536 * @param img image to get children for 7537 * 7538 * @return list of Contents (direct image children) 7539 * 7540 * @throws TskCoreException thrown if a critical error occurred within tsk 7541 * core 7542 */ 7543 List<Content> getImageChildren(Image img) throws TskCoreException { 7544 Collection<ObjectInfo> childInfos = getChildrenInfo(img); 7545 List<Content> children = new ArrayList<Content>(); 7546 for (ObjectInfo info : childInfos) { 7547 if (null != info.type) { 7548 switch (info.type) { 7549 case VS: 7550 children.add(getVolumeSystemById(info.id, img)); 7551 break; 7552 case FS: 7553 children.add(getFileSystemById(info.id, img)); 7554 break; 7555 case ABSTRACTFILE: 7556 AbstractFile f = getAbstractFileById(info.id); 7557 if (f != null) { 7558 children.add(f); 7559 } 7560 break; 7561 case ARTIFACT: 7562 BlackboardArtifact art = getArtifactById(info.id); 7563 if (art != null) { 7564 children.add(art); 7565 } 7566 break; 7567 case REPORT: 7568 // Do nothing for now - see JIRA-3673 7569 break; 7570 default: 7571 throw new TskCoreException("Image has child of invalid type: " + info.type); 7572 } 7573 } 7574 } 7575 return children; 7576 } 7577 7578 /** 7579 * Returns the list of direct children IDs for a given Image 7580 * 7581 * @param img image to get children for 7582 * 7583 * @return list of IDs (direct image children) 7584 * 7585 * @throws TskCoreException thrown if a critical error occurred within tsk 7586 * core 7587 */ 7588 List<Long> getImageChildrenIds(Image img) throws TskCoreException { 7589 Collection<ObjectInfo> childInfos = getChildrenInfo(img); 7590 List<Long> children = new ArrayList<Long>(); 7591 for (ObjectInfo info : childInfos) { 7592 if (info.type == ObjectType.VS 7593 || info.type == ObjectType.FS 7594 || info.type == ObjectType.ABSTRACTFILE 7595 || info.type == ObjectType.ARTIFACT) { 7596 children.add(info.id); 7597 } else if (info.type == ObjectType.REPORT) { 7598 // Do nothing for now - see JIRA-3673 7599 } else { 7600 throw new TskCoreException("Image has child of invalid type: " + info.type); 7601 } 7602 } 7603 return children; 7604 } 7605 7606 /** 7607 * Returns the list of direct children for a given VolumeSystem 7608 * 7609 * @param vs volume system to get children for 7610 * 7611 * @return list of volume system children objects 7612 * 7613 * @throws TskCoreException thrown if a critical error occurred within tsk 7614 * core 7615 */ 7616 List<Content> getVolumeSystemChildren(VolumeSystem vs) throws TskCoreException { 7617 Collection<ObjectInfo> childInfos = getChildrenInfo(vs); 7618 List<Content> children = new ArrayList<Content>(); 7619 for (ObjectInfo info : childInfos) { 7620 if (null != info.type) { 7621 switch (info.type) { 7622 case VOL: 7623 children.add(getVolumeById(info.id, vs)); 7624 break; 7625 case ABSTRACTFILE: 7626 AbstractFile f = getAbstractFileById(info.id); 7627 if (f != null) { 7628 children.add(f); 7629 } 7630 break; 7631 case ARTIFACT: 7632 BlackboardArtifact art = getArtifactById(info.id); 7633 if (art != null) { 7634 children.add(art); 7635 } 7636 break; 7637 default: 7638 throw new TskCoreException("VolumeSystem has child of invalid type: " + info.type); 7639 } 7640 } 7641 } 7642 return children; 7643 } 7644 7645 /** 7646 * Returns the list of direct children IDs for a given VolumeSystem 7647 * 7648 * @param vs volume system to get children for 7649 * 7650 * @return list of volume system children IDs 7651 * 7652 * @throws TskCoreException thrown if a critical error occurred within tsk 7653 * core 7654 */ 7655 List<Long> getVolumeSystemChildrenIds(VolumeSystem vs) throws TskCoreException { 7656 Collection<ObjectInfo> childInfos = getChildrenInfo(vs); 7657 List<Long> children = new ArrayList<Long>(); 7658 for (ObjectInfo info : childInfos) { 7659 if (info.type == ObjectType.VOL || info.type == ObjectType.ABSTRACTFILE || info.type == ObjectType.ARTIFACT) { 7660 children.add(info.id); 7661 } else { 7662 throw new TskCoreException("VolumeSystem has child of invalid type: " + info.type); 7663 } 7664 } 7665 return children; 7666 } 7667 7668 /** 7669 * Returns a list of direct children for a given Volume 7670 * 7671 * @param vol volume to get children of 7672 * 7673 * @return list of Volume children 7674 * 7675 * @throws TskCoreException thrown if a critical error occurred within tsk 7676 * core 7677 */ 7678 List<Content> getVolumeChildren(Volume vol) throws TskCoreException { 7679 Collection<ObjectInfo> childInfos = getChildrenInfo(vol); 7680 List<Content> children = new ArrayList<Content>(); 7681 for (ObjectInfo info : childInfos) { 7682 if (null != info.type) { 7683 switch (info.type) { 7684 case FS: 7685 children.add(getFileSystemById(info.id, vol)); 7686 break; 7687 case ABSTRACTFILE: 7688 AbstractFile f = getAbstractFileById(info.id); 7689 if (f != null) { 7690 children.add(f); 7691 } 7692 break; 7693 case ARTIFACT: 7694 BlackboardArtifact art = getArtifactById(info.id); 7695 if (art != null) { 7696 children.add(art); 7697 } 7698 break; 7699 default: 7700 throw new TskCoreException("Volume has child of invalid type: " + info.type); 7701 } 7702 } 7703 } 7704 return children; 7705 } 7706 7707 /** 7708 * Returns a list of direct children IDs for a given Volume 7709 * 7710 * @param vol volume to get children of 7711 * 7712 * @return list of Volume children IDs 7713 * 7714 * @throws TskCoreException thrown if a critical error occurred within tsk 7715 * core 7716 */ 7717 List<Long> getVolumeChildrenIds(Volume vol) throws TskCoreException { 7718 final Collection<ObjectInfo> childInfos = getChildrenInfo(vol); 7719 final List<Long> children = new ArrayList<Long>(); 7720 for (ObjectInfo info : childInfos) { 7721 if (info.type == ObjectType.FS || info.type == ObjectType.ABSTRACTFILE || info.type == ObjectType.ARTIFACT) { 7722 children.add(info.id); 7723 } else { 7724 throw new TskCoreException("Volume has child of invalid type: " + info.type); 7725 } 7726 } 7727 return children; 7728 } 7729 7730 /** 7731 * Adds an image to the case database. 7732 * 7733 * @param deviceObjId The object id of the device associated with the 7734 * image. 7735 * @param imageFilePaths The image file paths. 7736 * @param timeZone The time zone for the image. 7737 * 7738 * @return An Image object. 7739 * 7740 * @throws TskCoreException if there is an error adding the image to case 7741 * database. 7742 */ 7743 public Image addImageInfo(long deviceObjId, List<String> imageFilePaths, String timeZone) throws TskCoreException { 7744 long imageId = this.caseHandle.addImageInfo(deviceObjId, imageFilePaths, timeZone, this); 7745 return getImageById(imageId); 7746 } 7747 7748 /** 7749 * Returns a map of image object IDs to a list of fully qualified file paths 7750 * for that image 7751 * 7752 * @return map of image object IDs to file paths 7753 * 7754 * @throws TskCoreException thrown if a critical error occurred within tsk 7755 * core 7756 */ 7757 public Map<Long, List<String>> getImagePaths() throws TskCoreException { 7758 CaseDbConnection connection = connections.getConnection(); 7759 acquireSingleUserCaseReadLock(); 7760 Statement s1 = null; 7761 Statement s2 = null; 7762 ResultSet rs1 = null; 7763 ResultSet rs2 = null; 7764 try { 7765 s1 = connection.createStatement(); 7766 rs1 = connection.executeQuery(s1, "SELECT obj_id FROM tsk_image_info"); //NON-NLS 7767 s2 = connection.createStatement(); 7768 Map<Long, List<String>> imgPaths = new LinkedHashMap<Long, List<String>>(); 7769 while (rs1.next()) { 7770 long obj_id = rs1.getLong("obj_id"); //NON-NLS 7771 rs2 = connection.executeQuery(s2, "SELECT * FROM tsk_image_names WHERE obj_id = " + obj_id); //NON-NLS 7772 List<String> paths = new ArrayList<String>(); 7773 while (rs2.next()) { 7774 paths.add(rs2.getString("name")); 7775 } 7776 rs2.close(); 7777 rs2 = null; 7778 imgPaths.put(obj_id, paths); 7779 } 7780 return imgPaths; 7781 } catch (SQLException ex) { 7782 throw new TskCoreException("Error getting image paths.", ex); 7783 } finally { 7784 closeResultSet(rs2); 7785 closeStatement(s2); 7786 closeResultSet(rs1); 7787 closeStatement(s1); 7788 connection.close(); 7789 releaseSingleUserCaseReadLock(); 7790 } 7791 } 7792 7793 /** 7794 * Returns a list of fully qualified file paths based on an image object ID. 7795 * 7796 * @param objectId The object id of the data source. 7797 * 7798 * @return List of file paths. 7799 * 7800 * @throws TskCoreException Thrown if a critical error occurred within tsk 7801 * core 7802 */ 7803 private List<String> getImagePathsById(long objectId) throws TskCoreException { 7804 List<String> imagePaths = new ArrayList<String>(); 7805 CaseDbConnection connection = connections.getConnection(); 7806 acquireSingleUserCaseReadLock(); 7807 Statement statement = null; 7808 ResultSet resultSet = null; 7809 try { 7810 statement = connection.createStatement(); 7811 resultSet = connection.executeQuery(statement, "SELECT name FROM tsk_image_names WHERE tsk_image_names.obj_id = " + objectId); //NON-NLS 7812 while (resultSet.next()) { 7813 imagePaths.add(resultSet.getString("name")); 7814 } 7815 } catch (SQLException ex) { 7816 throw new TskCoreException(String.format("Error getting image names with obj_id = %d", objectId), ex); 7817 } finally { 7818 closeResultSet(resultSet); 7819 closeStatement(statement); 7820 connection.close(); 7821 releaseSingleUserCaseReadLock(); 7822 } 7823 7824 return imagePaths; 7825 } 7826 7827 /** 7828 * @return a collection of Images associated with this instance of 7829 * SleuthkitCase 7830 * 7831 * @throws TskCoreException 7832 */ 7833 public List<Image> getImages() throws TskCoreException { 7834 CaseDbConnection connection = connections.getConnection(); 7835 acquireSingleUserCaseReadLock(); 7836 Statement s = null; 7837 ResultSet rs = null; 7838 try { 7839 s = connection.createStatement(); 7840 rs = connection.executeQuery(s, "SELECT obj_id FROM tsk_image_info"); //NON-NLS 7841 Collection<Long> imageIDs = new ArrayList<Long>(); 7842 while (rs.next()) { 7843 imageIDs.add(rs.getLong("obj_id")); //NON-NLS 7844 } 7845 List<Image> images = new ArrayList<Image>(); 7846 for (long id : imageIDs) { 7847 images.add(getImageById(id)); 7848 } 7849 return images; 7850 } catch (SQLException ex) { 7851 throw new TskCoreException("Error retrieving images.", ex); 7852 } finally { 7853 closeResultSet(rs); 7854 closeStatement(s); 7855 connection.close(); 7856 releaseSingleUserCaseReadLock(); 7857 } 7858 } 7859 7860 /** 7861 * Set the file paths for the image given by obj_id 7862 * 7863 * @param obj_id the ID of the image to update 7864 * @param paths the fully qualified path to the files that make up the 7865 * image 7866 * 7867 * @throws TskCoreException exception thrown when critical error occurs 7868 * within tsk core and the update fails 7869 */ 7870 public void setImagePaths(long obj_id, List<String> paths) throws TskCoreException { 7871 CaseDbConnection connection = connections.getConnection(); 7872 acquireSingleUserCaseWriteLock(); 7873 PreparedStatement statement = null; 7874 try { 7875 connection.beginTransaction(); 7876 statement = connection.getPreparedStatement(PREPARED_STATEMENT.DELETE_IMAGE_NAME); 7877 statement.clearParameters(); 7878 statement.setLong(1, obj_id); 7879 connection.executeUpdate(statement); 7880 for (int i = 0; i < paths.size(); i++) { 7881 statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_IMAGE_NAME); 7882 statement.clearParameters(); 7883 statement.setLong(1, obj_id); 7884 statement.setString(2, paths.get(i)); 7885 statement.setLong(3, i); 7886 connection.executeUpdate(statement); 7887 } 7888 connection.commitTransaction(); 7889 } catch (SQLException ex) { 7890 connection.rollbackTransaction(); 7891 throw new TskCoreException("Error updating image paths.", ex); 7892 } finally { 7893 connection.close(); 7894 releaseSingleUserCaseWriteLock(); 7895 } 7896 } 7897 7898 /** 7899 * Creates file object from a SQL query result set of rows from the 7900 * tsk_files table. Assumes that the query was of the form "SELECT * FROM 7901 * tsk_files WHERE XYZ". 7902 * 7903 * @param rs ResultSet to get content from. Caller is responsible for 7904 * closing it. 7905 * 7906 * @return list of file objects from tsk_files table containing the files 7907 * 7908 * @throws SQLException if the query fails 7909 */ 7910 /** 7911 * Creates AbstractFile objects for the result set of a tsk_files table 7912 * query of the form "SELECT * FROM tsk_files WHERE XYZ". 7913 * 7914 * @param rs A result set from a query of the tsk_files table of the 7915 * form "SELECT * FROM tsk_files WHERE XYZ". 7916 * @param connection A case database connection. 7917 * 7918 * @return A list of AbstractFile objects. 7919 * 7920 * @throws SQLException Thrown if there is a problem iterating through the 7921 * record set. 7922 */ 7923 private List<AbstractFile> resultSetToAbstractFiles(ResultSet rs, CaseDbConnection connection) throws SQLException { 7924 ArrayList<AbstractFile> results = new ArrayList<AbstractFile>(); 7925 try { 7926 while (rs.next()) { 7927 final short type = rs.getShort("type"); //NON-NLS 7928 if (type == TSK_DB_FILES_TYPE_ENUM.FS.getFileType() 7929 && (rs.getShort("meta_type") != TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT_DIR.getValue())) { 7930 FsContent result; 7931 if (rs.getShort("meta_type") == TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()) { //NON-NLS 7932 result = directory(rs, null); 7933 } else { 7934 result = file(rs, null); 7935 } 7936 results.add(result); 7937 } else if (type == TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() 7938 || (rs.getShort("meta_type") == TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT_DIR.getValue())) { //NON-NLS 7939 final VirtualDirectory virtDir = virtualDirectory(rs, connection); 7940 results.add(virtDir); 7941 } else if (type == TSK_DB_FILES_TYPE_ENUM.LOCAL_DIR.getFileType()) { 7942 final LocalDirectory localDir = localDirectory(rs); 7943 results.add(localDir); 7944 } else if (type == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType() 7945 || type == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS.getFileType() 7946 || type == TSK_DB_FILES_TYPE_ENUM.CARVED.getFileType() 7947 || type == TSK_DB_FILES_TYPE_ENUM.LAYOUT_FILE.getFileType()) { 7948 TSK_DB_FILES_TYPE_ENUM atype = TSK_DB_FILES_TYPE_ENUM.valueOf(type); 7949 String parentPath = rs.getString("parent_path"); //NON-NLS 7950 if (parentPath == null) { 7951 parentPath = "/"; //NON-NLS 7952 } 7953 LayoutFile lf = new LayoutFile(this, 7954 rs.getLong("obj_id"), //NON-NLS 7955 rs.getLong("data_source_obj_id"), 7956 rs.getString("name"), //NON-NLS 7957 atype, 7958 TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS 7959 TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), rs.getShort("meta_flags"), //NON-NLS 7960 rs.getLong("size"), //NON-NLS 7961 rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS 7962 rs.getString("md5"), FileKnown.valueOf(rs.getByte("known")), parentPath, rs.getString("mime_type")); //NON-NLS 7963 results.add(lf); 7964 } else if (type == TSK_DB_FILES_TYPE_ENUM.DERIVED.getFileType()) { 7965 final DerivedFile df; 7966 df = derivedFile(rs, connection, AbstractContent.UNKNOWN_ID); 7967 results.add(df); 7968 } else if (type == TSK_DB_FILES_TYPE_ENUM.LOCAL.getFileType()) { 7969 final LocalFile lf; 7970 lf = localFile(rs, connection, AbstractContent.UNKNOWN_ID); 7971 results.add(lf); 7972 } else if (type == TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType()) { 7973 final SlackFile sf = slackFile(rs, null); 7974 results.add(sf); 7975 } 7976 } //end for each resultSet 7977 } catch (SQLException e) { 7978 logger.log(Level.SEVERE, "Error getting abstract files from result set", e); //NON-NLS 7979 } 7980 7981 return results; 7982 } 7983 7984 // This following methods generate AbstractFile objects from a ResultSet 7985 /** 7986 * Create a File object from the result set containing query results on 7987 * tsk_files table 7988 * 7989 * @param rs the result set 7990 * @param fs parent file system 7991 * 7992 * @return a newly create File 7993 * 7994 * @throws SQLException 7995 */ 7996 org.sleuthkit.datamodel.File file(ResultSet rs, FileSystem fs) throws SQLException { 7997 org.sleuthkit.datamodel.File f = new org.sleuthkit.datamodel.File(this, rs.getLong("obj_id"), //NON-NLS 7998 rs.getLong("data_source_obj_id"), rs.getLong("fs_obj_id"), //NON-NLS 7999 TskData.TSK_FS_ATTR_TYPE_ENUM.valueOf(rs.getShort("attr_type")), //NON-NLS 8000 rs.getInt("attr_id"), rs.getString("name"), rs.getLong("meta_addr"), rs.getInt("meta_seq"), //NON-NLS 8001 TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS 8002 TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS 8003 TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), //NON-NLS 8004 rs.getShort("meta_flags"), rs.getLong("size"), //NON-NLS 8005 rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS 8006 (short) rs.getInt("mode"), rs.getInt("uid"), rs.getInt("gid"), //NON-NLS 8007 rs.getString("md5"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS 8008 rs.getString("parent_path"), rs.getString("mime_type"), rs.getString("extension")); //NON-NLS 8009 f.setFileSystem(fs); 8010 return f; 8011 } 8012 8013 /** 8014 * Create a Directory object from the result set containing query results on 8015 * tsk_files table 8016 * 8017 * @param rs the result set 8018 * @param fs parent file system 8019 * 8020 * @return a newly created Directory object 8021 * 8022 * @throws SQLException thrown if SQL error occurred 8023 */ 8024 Directory directory(ResultSet rs, FileSystem fs) throws SQLException { 8025 Directory dir = new Directory(this, rs.getLong("obj_id"), rs.getLong("data_source_obj_id"), rs.getLong("fs_obj_id"), //NON-NLS 8026 TskData.TSK_FS_ATTR_TYPE_ENUM.valueOf(rs.getShort("attr_type")), //NON-NLS 8027 rs.getInt("attr_id"), rs.getString("name"), rs.getLong("meta_addr"), rs.getInt("meta_seq"), //NON-NLS 8028 TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS 8029 TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS 8030 TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), //NON-NLS 8031 rs.getShort("meta_flags"), rs.getLong("size"), //NON-NLS 8032 rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS 8033 rs.getShort("mode"), rs.getInt("uid"), rs.getInt("gid"), //NON-NLS 8034 rs.getString("md5"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS 8035 rs.getString("parent_path")); //NON-NLS 8036 dir.setFileSystem(fs); 8037 return dir; 8038 } 8039 8040 /** 8041 * Create a virtual directory object from a result set. 8042 * 8043 * @param rs the result set. 8044 * @param connection The case database connection. 8045 * 8046 * @return newly created VirtualDirectory object. 8047 * 8048 * @throws SQLException 8049 */ 8050 VirtualDirectory virtualDirectory(ResultSet rs, CaseDbConnection connection) throws SQLException { 8051 String parentPath = rs.getString("parent_path"); //NON-NLS 8052 if (parentPath == null) { 8053 parentPath = ""; 8054 } 8055 8056 long objId = rs.getLong("obj_id"); 8057 long dsObjId = rs.getLong("data_source_obj_id"); 8058 if (objId == dsObjId) { // virtual directory is a data source 8059 8060 String deviceId = ""; 8061 String timeZone = ""; 8062 Statement s = null; 8063 ResultSet rsDataSourceInfo = null; 8064 8065 acquireSingleUserCaseReadLock(); 8066 try { 8067 s = connection.createStatement(); 8068 rsDataSourceInfo = connection.executeQuery(s, "SELECT device_id, time_zone FROM data_source_info WHERE obj_id = " + objId); 8069 if (rsDataSourceInfo.next()) { 8070 deviceId = rsDataSourceInfo.getString("device_id"); 8071 timeZone = rsDataSourceInfo.getString("time_zone"); 8072 } 8073 } catch (SQLException ex) { 8074 logger.log(Level.SEVERE, "Error data source info for datasource id " + objId, ex); //NON-NLS 8075 } finally { 8076 closeResultSet(rsDataSourceInfo); 8077 closeStatement(s); 8078 releaseSingleUserCaseReadLock(); 8079 } 8080 8081 return new LocalFilesDataSource(this, 8082 objId, dsObjId, 8083 deviceId, 8084 rs.getString("name"), 8085 TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS 8086 TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS 8087 TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), 8088 rs.getShort("meta_flags"), 8089 timeZone, 8090 rs.getString("md5"), 8091 FileKnown.valueOf(rs.getByte("known")), 8092 parentPath); 8093 } else { 8094 final VirtualDirectory vd = new VirtualDirectory(this, 8095 objId, dsObjId, 8096 rs.getString("name"), //NON-NLS 8097 TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS 8098 TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS 8099 TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), //NON-NLS 8100 rs.getShort("meta_flags"), rs.getString("md5"), //NON-NLS 8101 FileKnown.valueOf(rs.getByte("known")), parentPath); //NON-NLS 8102 return vd; 8103 } 8104 } 8105 8106 /** 8107 * Create a virtual directory object from a result set 8108 * 8109 * @param rs the result set 8110 * 8111 * @return newly created VirtualDirectory object 8112 * 8113 * @throws SQLException 8114 */ 8115 LocalDirectory localDirectory(ResultSet rs) throws SQLException { 8116 String parentPath = rs.getString("parent_path"); //NON-NLS 8117 if (parentPath == null) { 8118 parentPath = ""; 8119 } 8120 final LocalDirectory ld = new LocalDirectory(this, rs.getLong("obj_id"), //NON-NLS 8121 rs.getLong("data_source_obj_id"), rs.getString("name"), //NON-NLS 8122 TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS 8123 TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS 8124 TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), //NON-NLS 8125 rs.getShort("meta_flags"), rs.getString("md5"), //NON-NLS 8126 FileKnown.valueOf(rs.getByte("known")), parentPath); //NON-NLS 8127 return ld; 8128 } 8129 8130 /** 8131 * Creates a DerivedFile object using the values of a given result set. 8132 * 8133 * @param rs The result set. 8134 * @param connection The case database connection. 8135 * @param parentId The parent id for the derived file or 8136 * AbstractContent.UNKNOWN_ID. 8137 * 8138 * @return The DerivedFile object. 8139 * 8140 * @throws SQLException if there is an error reading from the result set or 8141 * doing additional queries. 8142 */ 8143 private DerivedFile derivedFile(ResultSet rs, CaseDbConnection connection, long parentId) throws SQLException { 8144 boolean hasLocalPath = rs.getBoolean("has_path"); //NON-NLS 8145 long objId = rs.getLong("obj_id"); //NON-NLS 8146 String localPath = null; 8147 TskData.EncodingType encodingType = TskData.EncodingType.NONE; 8148 if (hasLocalPath) { 8149 ResultSet rsFilePath = null; 8150 acquireSingleUserCaseReadLock(); 8151 try { 8152 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_LOCAL_PATH_AND_ENCODING_FOR_FILE); 8153 statement.clearParameters(); 8154 statement.setLong(1, objId); 8155 rsFilePath = connection.executeQuery(statement); 8156 if (rsFilePath.next()) { 8157 localPath = rsFilePath.getString("path"); 8158 encodingType = TskData.EncodingType.valueOf(rsFilePath.getInt("encoding_type")); 8159 } 8160 } catch (SQLException ex) { 8161 logger.log(Level.SEVERE, "Error getting encoding type for file " + objId, ex); //NON-NLS 8162 } finally { 8163 closeResultSet(rsFilePath); 8164 releaseSingleUserCaseReadLock(); 8165 } 8166 } 8167 String parentPath = rs.getString("parent_path"); //NON-NLS 8168 if (parentPath == null) { 8169 parentPath = ""; 8170 } 8171 final DerivedFile df = new DerivedFile(this, objId, rs.getLong("data_source_obj_id"), 8172 rs.getString("name"), //NON-NLS 8173 TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS 8174 TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS 8175 TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), rs.getShort("meta_flags"), //NON-NLS 8176 rs.getLong("size"), //NON-NLS 8177 rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS 8178 rs.getString("md5"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS 8179 parentPath, localPath, parentId, rs.getString("mime_type"), 8180 encodingType, rs.getString("extension")); 8181 return df; 8182 } 8183 8184 /** 8185 * Creates a LocalFile object using the data from a given result set. 8186 * 8187 * @param rs The result set. 8188 * @param connection The case database connection. 8189 * @param parentId The parent id for the derived file or 8190 * AbstractContent.UNKNOWN_ID. 8191 * 8192 * @return The LocalFile object. 8193 * 8194 * @throws SQLException if there is an error reading from the result set or 8195 * doing additional queries. 8196 */ 8197 private LocalFile localFile(ResultSet rs, CaseDbConnection connection, long parentId) throws SQLException { 8198 long objId = rs.getLong("obj_id"); //NON-NLS 8199 String localPath = null; 8200 TskData.EncodingType encodingType = TskData.EncodingType.NONE; 8201 if (rs.getBoolean("has_path")) { 8202 ResultSet rsFilePath = null; 8203 acquireSingleUserCaseReadLock(); 8204 try { 8205 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_LOCAL_PATH_AND_ENCODING_FOR_FILE); 8206 statement.clearParameters(); 8207 statement.setLong(1, objId); 8208 rsFilePath = connection.executeQuery(statement); 8209 if (rsFilePath.next()) { 8210 localPath = rsFilePath.getString("path"); 8211 encodingType = TskData.EncodingType.valueOf(rsFilePath.getInt("encoding_type")); 8212 } 8213 } catch (SQLException ex) { 8214 logger.log(Level.SEVERE, "Error getting encoding type for file " + objId, ex); //NON-NLS 8215 } finally { 8216 closeResultSet(rsFilePath); 8217 releaseSingleUserCaseReadLock(); 8218 } 8219 } 8220 String parentPath = rs.getString("parent_path"); //NON-NLS 8221 if (null == parentPath) { 8222 parentPath = ""; 8223 } 8224 LocalFile file = new LocalFile(this, objId, rs.getString("name"), //NON-NLS 8225 TSK_DB_FILES_TYPE_ENUM.valueOf(rs.getShort("type")), //NON-NLS 8226 TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS 8227 TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS 8228 TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), rs.getShort("meta_flags"), //NON-NLS 8229 rs.getLong("size"), //NON-NLS 8230 rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS 8231 rs.getString("mime_type"), rs.getString("md5"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS 8232 parentId, parentPath, rs.getLong("data_source_obj_id"), 8233 localPath, encodingType, rs.getString("extension")); 8234 return file; 8235 } 8236 8237 /** 8238 * Create a Slack File object from the result set containing query results 8239 * on tsk_files table 8240 * 8241 * @param rs the result set 8242 * @param fs parent file system 8243 * 8244 * @return a newly created Slack File 8245 * 8246 * @throws SQLException 8247 */ 8248 org.sleuthkit.datamodel.SlackFile slackFile(ResultSet rs, FileSystem fs) throws SQLException { 8249 org.sleuthkit.datamodel.SlackFile f = new org.sleuthkit.datamodel.SlackFile(this, rs.getLong("obj_id"), //NON-NLS 8250 rs.getLong("data_source_obj_id"), rs.getLong("fs_obj_id"), //NON-NLS 8251 TskData.TSK_FS_ATTR_TYPE_ENUM.valueOf(rs.getShort("attr_type")), //NON-NLS 8252 rs.getInt("attr_id"), rs.getString("name"), rs.getLong("meta_addr"), rs.getInt("meta_seq"), //NON-NLS 8253 TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), //NON-NLS 8254 TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), //NON-NLS 8255 TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), //NON-NLS 8256 rs.getShort("meta_flags"), rs.getLong("size"), //NON-NLS 8257 rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), //NON-NLS 8258 (short) rs.getInt("mode"), rs.getInt("uid"), rs.getInt("gid"), //NON-NLS 8259 rs.getString("md5"), FileKnown.valueOf(rs.getByte("known")), //NON-NLS 8260 rs.getString("parent_path"), rs.getString("mime_type"), rs.getString("extension")); //NON-NLS 8261 f.setFileSystem(fs); 8262 return f; 8263 } 8264 8265 /** 8266 * Returns the list of abstractFile objects from a result of selecting many 8267 * files that meet a certain criteria. 8268 * 8269 * @param rs 8270 * @param parentId 8271 * 8272 * @return 8273 * 8274 * @throws SQLException 8275 */ 8276 List<Content> fileChildren(ResultSet rs, CaseDbConnection connection, long parentId) throws SQLException { 8277 List<Content> children = new ArrayList<Content>(); 8278 8279 while (rs.next()) { 8280 TskData.TSK_DB_FILES_TYPE_ENUM type = TskData.TSK_DB_FILES_TYPE_ENUM.valueOf(rs.getShort("type")); 8281 8282 if (null != type) { 8283 switch (type) { 8284 case FS: 8285 if (rs.getShort("meta_type") != TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT_DIR.getValue()) { 8286 FsContent result; 8287 if (rs.getShort("meta_type") == TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()) { 8288 result = directory(rs, null); 8289 } else { 8290 result = file(rs, null); 8291 } 8292 children.add(result); 8293 } else { 8294 VirtualDirectory virtDir = virtualDirectory(rs, connection); 8295 children.add(virtDir); 8296 } 8297 break; 8298 case VIRTUAL_DIR: 8299 VirtualDirectory virtDir = virtualDirectory(rs, connection); 8300 children.add(virtDir); 8301 break; 8302 case LOCAL_DIR: 8303 LocalDirectory localDir = localDirectory(rs); 8304 children.add(localDir); 8305 break; 8306 case UNALLOC_BLOCKS: 8307 case UNUSED_BLOCKS: 8308 case CARVED: 8309 case LAYOUT_FILE: { 8310 String parentPath = rs.getString("parent_path"); 8311 if (parentPath == null) { 8312 parentPath = ""; 8313 } 8314 final LayoutFile lf = new LayoutFile(this, rs.getLong("obj_id"), 8315 rs.getLong("data_source_obj_id"), rs.getString("name"), type, 8316 TSK_FS_NAME_TYPE_ENUM.valueOf(rs.getShort("dir_type")), 8317 TSK_FS_META_TYPE_ENUM.valueOf(rs.getShort("meta_type")), 8318 TSK_FS_NAME_FLAG_ENUM.valueOf(rs.getShort("dir_flags")), rs.getShort("meta_flags"), 8319 rs.getLong("size"), 8320 rs.getLong("ctime"), rs.getLong("crtime"), rs.getLong("atime"), rs.getLong("mtime"), 8321 rs.getString("md5"), 8322 FileKnown.valueOf(rs.getByte("known")), parentPath, rs.getString("mime_type")); 8323 children.add(lf); 8324 break; 8325 } 8326 case DERIVED: 8327 final DerivedFile df = derivedFile(rs, connection, parentId); 8328 children.add(df); 8329 break; 8330 case LOCAL: { 8331 final LocalFile lf = localFile(rs, connection, parentId); 8332 children.add(lf); 8333 break; 8334 } 8335 case SLACK: { 8336 final SlackFile sf = slackFile(rs, null); 8337 children.add(sf); 8338 break; 8339 } 8340 default: 8341 break; 8342 } 8343 } 8344 } 8345 return children; 8346 } 8347 8348 /** 8349 * Creates BlackboardArtifact objects for the result set of a 8350 * blackboard_artifacts table query of the form "SELECT * FROM 8351 * blackboard_artifacts WHERE XYZ". 8352 * 8353 * @param rs A result set from a query of the blackboard_artifacts table of 8354 * the form "SELECT * FROM blackboard_artifacts WHERE XYZ". 8355 * 8356 * @return A list of BlackboardArtifact objects. 8357 * 8358 * @throws SQLException Thrown if there is a problem iterating through 8359 * the result set. 8360 * @throws TskCoreException Thrown if there is an error looking up the 8361 * artifact type id 8362 */ 8363 private List<BlackboardArtifact> resultSetToArtifacts(ResultSet rs) throws SQLException, TskCoreException { 8364 ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); 8365 try { 8366 while (rs.next()) { 8367 BlackboardArtifact.Type artifactType = getArtifactType(rs.getInt("artifact_type_id")); 8368 if (artifactType != null) { 8369 artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"), 8370 rs.getInt("artifact_type_id"), artifactType.getTypeName(), artifactType.getDisplayName(), 8371 BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); 8372 } else { 8373 throw new TskCoreException("Error looking up artifact type ID " + rs.getInt("artifact_type_id") + " from artifact " + rs.getLong("artifact_id")); 8374 } 8375 } //end for each resultSet 8376 } catch (SQLException e) { 8377 logger.log(Level.SEVERE, "Error getting artifacts from result set", e); //NON-NLS 8378 } 8379 8380 return artifacts; 8381 } 8382 8383 /** 8384 * This method allows developers to run arbitrary SQL "SELECT" queries. The 8385 * CaseDbQuery object will take care of acquiring the necessary database 8386 * lock and when used in a try-with-resources block will automatically take 8387 * care of releasing the lock. If you do not use a try-with-resources block 8388 * you must call CaseDbQuery.close() once you are done processing the files 8389 * of the query. 8390 * 8391 * Also note that if you use it within a transaction to insert something 8392 * into the database, and then within that same transaction query the 8393 * inserted item from the database, you will likely not see your inserted 8394 * item, as the method uses new connections for each execution. With this 8395 * method, you must close your transaction before successfully querying for 8396 * newly-inserted items. 8397 * 8398 * @param query The query string to execute. 8399 * 8400 * @return A CaseDbQuery instance. 8401 * 8402 * @throws TskCoreException 8403 */ 8404 public CaseDbQuery executeQuery(String query) throws TskCoreException { 8405 return new CaseDbQuery(query); 8406 } 8407 8408 /** 8409 * This method allows developers to run arbitrary SQL queries, including 8410 * INSERT and UPDATE. The CaseDbQuery object will take care of acquiring the 8411 * necessary database lock and when used in a try-with-resources block will 8412 * automatically take care of releasing the lock. If you do not use a 8413 * try-with-resources block you must call CaseDbQuery.close() once you are 8414 * done processing the files of the query. 8415 * 8416 * Also note that if you use it within a transaction to insert something 8417 * into the database, and then within that same transaction query the 8418 * inserted item from the database, you will likely not see your inserted 8419 * item, as the method uses new connections for each execution. With this 8420 * method, you must close your transaction before successfully querying for 8421 * newly-inserted items. 8422 * 8423 * @param query The query string to execute. 8424 * 8425 * @return A CaseDbQuery instance. 8426 * 8427 * @throws TskCoreException 8428 */ 8429 public CaseDbQuery executeInsertOrUpdate(String query) throws TskCoreException { 8430 return new CaseDbQuery(query, true); 8431 } 8432 8433 /** 8434 * Get a case database connection. 8435 * 8436 * @return The case database connection. 8437 * 8438 * @throws TskCoreException 8439 */ 8440 CaseDbConnection getConnection() throws TskCoreException { 8441 return connections.getConnection(); 8442 } 8443 8444 SleuthkitJNI.CaseDbHandle getCaseHandle() { 8445 return this.caseHandle; 8446 } 8447 8448 @Override 8449 protected void finalize() throws Throwable { 8450 try { 8451 close(); 8452 } finally { 8453 super.finalize(); 8454 } 8455 } 8456 8457 /** 8458 * Call to free resources when done with instance. 8459 */ 8460 public synchronized void close() { 8461 acquireSingleUserCaseWriteLock(); 8462 8463 try { 8464 connections.close(); 8465 } catch (TskCoreException ex) { 8466 logger.log(Level.SEVERE, "Error closing database connection pool.", ex); //NON-NLS 8467 } 8468 8469 fileSystemIdMap.clear(); 8470 8471 try { 8472 if (this.caseHandle != null) { 8473 this.caseHandle.free(); 8474 this.caseHandle = null; 8475 } 8476 } catch (TskCoreException ex) { 8477 logger.log(Level.SEVERE, "Error freeing case handle.", ex); //NON-NLS 8478 } finally { 8479 releaseSingleUserCaseWriteLock(); 8480 } 8481 } 8482 8483 /** 8484 * Store the known status for the FsContent in the database Note: will not 8485 * update status if content is already 'Known Bad' 8486 * 8487 * @param file The AbstractFile object 8488 * @param fileKnown The object's known status 8489 * 8490 * @return true if the known status was updated, false otherwise 8491 * 8492 * @throws TskCoreException thrown if a critical error occurred within tsk 8493 * core 8494 */ 8495 public boolean setKnown(AbstractFile file, FileKnown fileKnown) throws TskCoreException { 8496 long id = file.getId(); 8497 FileKnown currentKnown = file.getKnown(); 8498 if (currentKnown.compareTo(fileKnown) > 0) { 8499 return false; 8500 } 8501 CaseDbConnection connection = connections.getConnection(); 8502 acquireSingleUserCaseWriteLock(); 8503 Statement statement = null; 8504 try { 8505 statement = connection.createStatement(); 8506 connection.executeUpdate(statement, "UPDATE tsk_files " //NON-NLS 8507 + "SET known='" + fileKnown.getFileKnownValue() + "' " //NON-NLS 8508 + "WHERE obj_id=" + id); //NON-NLS 8509 8510 file.setKnown(fileKnown); 8511 } catch (SQLException ex) { 8512 throw new TskCoreException("Error setting Known status.", ex); 8513 } finally { 8514 closeStatement(statement); 8515 connection.close(); 8516 releaseSingleUserCaseWriteLock(); 8517 } 8518 return true; 8519 } 8520 8521 /** 8522 * Set the name of an object in the tsk_files table. 8523 * 8524 * @param name The new name for the object 8525 * @param objId The object ID 8526 * 8527 * @throws TskCoreException If there is an error updating the case database. 8528 */ 8529 void setFileName(String name, long objId) throws TskCoreException { 8530 8531 CaseDbConnection connection = connections.getConnection(); 8532 acquireSingleUserCaseWriteLock(); 8533 try { 8534 PreparedStatement preparedStatement = connection.getPreparedStatement(SleuthkitCase.PREPARED_STATEMENT.UPDATE_FILE_NAME); 8535 preparedStatement.clearParameters(); 8536 preparedStatement.setString(1, name); 8537 preparedStatement.setLong(2, objId); 8538 connection.executeUpdate(preparedStatement); 8539 } catch (SQLException ex) { 8540 throw new TskCoreException(String.format("Error updating while the name for object ID %d to %s", objId, name), ex); 8541 } finally { 8542 connection.close(); 8543 releaseSingleUserCaseWriteLock(); 8544 } 8545 } 8546 8547 /** 8548 * Set the display name of an image in the tsk_image_info table. 8549 * 8550 * @param name The new name for the image 8551 * @param objId The object ID 8552 * 8553 * @throws TskCoreException If there is an error updating the case database. 8554 */ 8555 void setImageName(String name, long objId) throws TskCoreException { 8556 8557 CaseDbConnection connection = connections.getConnection(); 8558 acquireSingleUserCaseWriteLock(); 8559 try { 8560 PreparedStatement preparedStatement = connection.getPreparedStatement(SleuthkitCase.PREPARED_STATEMENT.UPDATE_IMAGE_NAME); 8561 preparedStatement.clearParameters(); 8562 preparedStatement.setString(1, name); 8563 preparedStatement.setLong(2, objId); 8564 connection.executeUpdate(preparedStatement); 8565 } catch (SQLException ex) { 8566 throw new TskCoreException(String.format("Error updating while the name for object ID %d to %s", objId, name), ex); 8567 } finally { 8568 connection.close(); 8569 releaseSingleUserCaseWriteLock(); 8570 } 8571 } 8572 8573 /** 8574 * Stores the MIME type of a file in the case database and updates the MIME 8575 * type of the given file object. 8576 * 8577 * @param file A file. 8578 * @param mimeType The MIME type. 8579 * 8580 * @throws TskCoreException If there is an error updating the case database. 8581 */ 8582 public void setFileMIMEType(AbstractFile file, String mimeType) throws TskCoreException { 8583 CaseDbConnection connection = connections.getConnection(); 8584 Statement statement = null; 8585 ResultSet rs = null; 8586 acquireSingleUserCaseWriteLock(); 8587 try { 8588 statement = connection.createStatement(); 8589 connection.executeUpdate(statement, String.format("UPDATE tsk_files SET mime_type = '%s' WHERE obj_id = %d", mimeType, file.getId())); 8590 file.setMIMEType(mimeType); 8591 } catch (SQLException ex) { 8592 throw new TskCoreException(String.format("Error setting MIME type for file (obj_id = %s)", file.getId()), ex); 8593 } finally { 8594 closeResultSet(rs); 8595 closeStatement(statement); 8596 connection.close(); 8597 releaseSingleUserCaseWriteLock(); 8598 } 8599 } 8600 8601 /** 8602 * Store the md5Hash for the file in the database 8603 * 8604 * @param file The file object 8605 * @param md5Hash The object's md5Hash 8606 * 8607 * @throws TskCoreException thrown if a critical error occurred within tsk 8608 * core 8609 */ 8610 void setMd5Hash(AbstractFile file, String md5Hash) throws TskCoreException { 8611 if (md5Hash == null) { 8612 return; 8613 } 8614 long id = file.getId(); 8615 CaseDbConnection connection = connections.getConnection(); 8616 acquireSingleUserCaseWriteLock(); 8617 try { 8618 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.UPDATE_FILE_MD5); 8619 statement.clearParameters(); 8620 statement.setString(1, md5Hash.toLowerCase()); 8621 statement.setLong(2, id); 8622 connection.executeUpdate(statement); 8623 file.setMd5Hash(md5Hash.toLowerCase()); 8624 } catch (SQLException ex) { 8625 throw new TskCoreException("Error setting MD5 hash", ex); 8626 } finally { 8627 connection.close(); 8628 releaseSingleUserCaseWriteLock(); 8629 } 8630 } 8631 8632 /** 8633 * Store the MD5 hash for the image in the database 8634 * 8635 * @param img The image object 8636 * @param md5Hash The image's MD5 hash 8637 * 8638 * @throws TskCoreException thrown if a critical error occurred within tsk 8639 * core 8640 */ 8641 void setMd5ImageHash(Image img, String md5Hash) throws TskCoreException { 8642 if (md5Hash == null) { 8643 return; 8644 } 8645 long id = img.getId(); 8646 CaseDbConnection connection = connections.getConnection(); 8647 acquireSingleUserCaseWriteLock(); 8648 try { 8649 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.UPDATE_IMAGE_MD5); 8650 statement.clearParameters(); 8651 statement.setString(1, md5Hash.toLowerCase()); 8652 statement.setLong(2, id); 8653 connection.executeUpdate(statement); 8654 } catch (SQLException ex) { 8655 throw new TskCoreException("Error setting MD5 hash", ex); 8656 } finally { 8657 connection.close(); 8658 releaseSingleUserCaseWriteLock(); 8659 } 8660 } 8661 8662 /** 8663 * Get the MD5 hash of an image from the case database 8664 * 8665 * @param The image object 8666 * 8667 * @return The image's MD5 hash 8668 * 8669 * @throws TskCoreException thrown if a critical error occurred within tsk 8670 * core 8671 */ 8672 String getMd5ImageHash(Image img) throws TskCoreException { 8673 long id = img.getId(); 8674 CaseDbConnection connection = connections.getConnection(); 8675 acquireSingleUserCaseReadLock(); 8676 ResultSet rs = null; 8677 String hash = ""; 8678 try { 8679 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_IMAGE_MD5); 8680 statement.clearParameters(); 8681 statement.setLong(1, id); 8682 rs = connection.executeQuery(statement); 8683 if (rs.next()) { 8684 hash = rs.getString("md5"); 8685 } 8686 return hash; 8687 } catch (SQLException ex) { 8688 throw new TskCoreException("Error getting MD5 hash", ex); 8689 } finally { 8690 closeResultSet(rs); 8691 connection.close(); 8692 releaseSingleUserCaseReadLock(); 8693 } 8694 } 8695 8696 /** 8697 * Store the SHA1 hash for the image in the database 8698 * 8699 * @param img The image object 8700 * @param sha1Hash The image's sha1 hash 8701 * 8702 * @throws TskCoreException thrown if a critical error occurred within tsk 8703 * core 8704 */ 8705 void setSha1ImageHash(Image img, String sha1Hash) throws TskCoreException { 8706 if (sha1Hash == null) { 8707 return; 8708 } 8709 long id = img.getId(); 8710 CaseDbConnection connection = connections.getConnection(); 8711 acquireSingleUserCaseWriteLock(); 8712 try { 8713 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.UPDATE_IMAGE_SHA1); 8714 statement.clearParameters(); 8715 statement.setString(1, sha1Hash.toLowerCase()); 8716 statement.setLong(2, id); 8717 connection.executeUpdate(statement); 8718 } catch (SQLException ex) { 8719 throw new TskCoreException("Error setting SHA1 hash", ex); 8720 } finally { 8721 connection.close(); 8722 releaseSingleUserCaseWriteLock(); 8723 } 8724 } 8725 8726 /** 8727 * Get the SHA1 hash of an image from the case database 8728 * 8729 * @param The image object 8730 * 8731 * @return The image's SHA1 hash 8732 * 8733 * @throws TskCoreException thrown if a critical error occurred within tsk 8734 * core 8735 */ 8736 String getSha1ImageHash(Image img) throws TskCoreException { 8737 long id = img.getId(); 8738 CaseDbConnection connection = connections.getConnection(); 8739 acquireSingleUserCaseReadLock(); 8740 ResultSet rs = null; 8741 String hash = ""; 8742 try { 8743 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_IMAGE_SHA1); 8744 statement.clearParameters(); 8745 statement.setLong(1, id); 8746 rs = connection.executeQuery(statement); 8747 if (rs.next()) { 8748 hash = rs.getString("sha1"); 8749 } 8750 return hash; 8751 } catch (SQLException ex) { 8752 throw new TskCoreException("Error getting SHA1 hash", ex); 8753 } finally { 8754 closeResultSet(rs); 8755 connection.close(); 8756 releaseSingleUserCaseReadLock(); 8757 } 8758 } 8759 8760 /** 8761 * Store the SHA256 hash for the file in the database 8762 * 8763 * @param img The image object 8764 * @param sha256Hash The object's md5Hash 8765 * 8766 * @throws TskCoreException thrown if a critical error occurred within tsk 8767 * core 8768 */ 8769 void setSha256ImageHash(Image img, String sha256Hash) throws TskCoreException { 8770 if (sha256Hash == null) { 8771 return; 8772 } 8773 long id = img.getId(); 8774 CaseDbConnection connection = connections.getConnection(); 8775 acquireSingleUserCaseWriteLock(); 8776 try { 8777 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.UPDATE_IMAGE_SHA256); 8778 statement.clearParameters(); 8779 statement.setString(1, sha256Hash.toLowerCase()); 8780 statement.setLong(2, id); 8781 connection.executeUpdate(statement); 8782 } catch (SQLException ex) { 8783 throw new TskCoreException("Error setting SHA256 hash", ex); 8784 } finally { 8785 connection.close(); 8786 releaseSingleUserCaseWriteLock(); 8787 } 8788 } 8789 8790 /** 8791 * Get the SHA256 hash of an image from the case database 8792 * 8793 * @param The image object 8794 * 8795 * @return The image's SHA256 hash 8796 * 8797 * @throws TskCoreException thrown if a critical error occurred within tsk 8798 * core 8799 */ 8800 String getSha256ImageHash(Image img) throws TskCoreException { 8801 long id = img.getId(); 8802 CaseDbConnection connection = connections.getConnection(); 8803 acquireSingleUserCaseReadLock(); 8804 ResultSet rs = null; 8805 String hash = ""; 8806 try { 8807 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_IMAGE_SHA256); 8808 statement.clearParameters(); 8809 statement.setLong(1, id); 8810 rs = connection.executeQuery(statement); 8811 if (rs.next()) { 8812 hash = rs.getString("sha256"); 8813 } 8814 return hash; 8815 } catch (SQLException ex) { 8816 throw new TskCoreException("Error setting SHA256 hash", ex); 8817 } finally { 8818 closeResultSet(rs); 8819 connection.close(); 8820 releaseSingleUserCaseReadLock(); 8821 } 8822 } 8823 8824 /** 8825 * Set the acquisition details in the data_source_info table 8826 * 8827 * @param datasource The data source 8828 * @param details The acquisition details 8829 * 8830 * @throws TskCoreException Thrown if the database write fails 8831 */ 8832 void setAcquisitionDetails(DataSource datasource, String details) throws TskCoreException { 8833 8834 long id = datasource.getId(); 8835 CaseDbConnection connection = connections.getConnection(); 8836 acquireSingleUserCaseWriteLock(); 8837 try { 8838 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.UPDATE_ACQUISITION_DETAILS); 8839 statement.clearParameters(); 8840 statement.setString(1, details); 8841 statement.setLong(2, id); 8842 connection.executeUpdate(statement); 8843 } catch (SQLException ex) { 8844 throw new TskCoreException("Error setting acquisition details", ex); 8845 } finally { 8846 connection.close(); 8847 releaseSingleUserCaseWriteLock(); 8848 } 8849 } 8850 8851 /** 8852 * Get the acquisition details from the data_source_info table 8853 * 8854 * @param datasource The data source 8855 * 8856 * @return The acquisition details 8857 * 8858 * @throws TskCoreException Thrown if the database read fails 8859 */ 8860 String getAcquisitionDetails(DataSource datasource) throws TskCoreException { 8861 long id = datasource.getId(); 8862 CaseDbConnection connection = connections.getConnection(); 8863 acquireSingleUserCaseReadLock(); 8864 ResultSet rs = null; 8865 String hash = ""; 8866 try { 8867 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ACQUISITION_DETAILS); 8868 statement.clearParameters(); 8869 statement.setLong(1, id); 8870 rs = connection.executeQuery(statement); 8871 if (rs.next()) { 8872 hash = rs.getString("acquisition_details"); 8873 } 8874 return hash; 8875 } catch (SQLException ex) { 8876 throw new TskCoreException("Error setting acquisition details", ex); 8877 } finally { 8878 closeResultSet(rs); 8879 connection.close(); 8880 releaseSingleUserCaseReadLock(); 8881 } 8882 } 8883 8884 /** 8885 * Set the review status of the given artifact to newStatus 8886 * 8887 * @param artifact The artifact whose review status is being set. 8888 * @param newStatus The new review status for the given artifact. Must not 8889 * be null. 8890 * 8891 * @throws TskCoreException thrown if a critical error occurred within tsk 8892 * core 8893 */ 8894 public void setReviewStatus(BlackboardArtifact artifact, BlackboardArtifact.ReviewStatus newStatus) throws TskCoreException { 8895 if (newStatus == null) { 8896 return; 8897 } 8898 CaseDbConnection connection = connections.getConnection(); 8899 acquireSingleUserCaseWriteLock(); 8900 Statement statement = null; 8901 try { 8902 statement = connection.createStatement(); 8903 connection.executeUpdate(statement, "UPDATE blackboard_artifacts " 8904 + " SET review_status_id=" + newStatus.getID() 8905 + " WHERE blackboard_artifacts.artifact_id = " + artifact.getArtifactID()); 8906 } catch (SQLException ex) { 8907 throw new TskCoreException("Error setting review status", ex); 8908 } finally { 8909 closeStatement(statement); 8910 connection.close(); 8911 releaseSingleUserCaseWriteLock(); 8912 } 8913 } 8914 8915 /** 8916 * Return the number of objects in the database of a given file type. 8917 * 8918 * @param contentType Type of file to count 8919 * 8920 * @return Number of objects with that type. 8921 * 8922 * @throws TskCoreException thrown if a critical error occurred within tsk 8923 * core 8924 */ 8925 public int countFsContentType(TskData.TSK_FS_META_TYPE_ENUM contentType) throws TskCoreException { 8926 CaseDbConnection connection = connections.getConnection(); 8927 acquireSingleUserCaseReadLock(); 8928 Statement s = null; 8929 ResultSet rs = null; 8930 try { 8931 s = connection.createStatement(); 8932 Short contentShort = contentType.getValue(); 8933 rs = connection.executeQuery(s, "SELECT COUNT(*) AS count FROM tsk_files WHERE meta_type = '" + contentShort.toString() + "'"); //NON-NLS 8934 int count = 0; 8935 if (rs.next()) { 8936 count = rs.getInt("count"); 8937 } 8938 return count; 8939 } catch (SQLException ex) { 8940 throw new TskCoreException("Error getting number of objects.", ex); 8941 } finally { 8942 closeResultSet(rs); 8943 closeStatement(s); 8944 connection.close(); 8945 releaseSingleUserCaseReadLock(); 8946 } 8947 } 8948 8949 /** 8950 * Escape the single quotes in the given string so they can be added to the 8951 * SQL caseDbConnection 8952 * 8953 * @param text 8954 * 8955 * @return text the escaped version 8956 */ 8957 public static String escapeSingleQuotes(String text) { 8958 String escapedText = null; 8959 if (text != null) { 8960 escapedText = text.replaceAll("'", "''"); 8961 } 8962 return escapedText; 8963 } 8964 8965 /** 8966 * Find all the files with the given MD5 hash. 8967 * 8968 * @param md5Hash hash value to match files with 8969 * 8970 * @return List of AbstractFile with the given hash 8971 */ 8972 public List<AbstractFile> findFilesByMd5(String md5Hash) { 8973 if (md5Hash == null) { 8974 return Collections.<AbstractFile>emptyList(); 8975 } 8976 CaseDbConnection connection; 8977 try { 8978 connection = connections.getConnection(); 8979 } catch (TskCoreException ex) { 8980 logger.log(Level.SEVERE, "Error finding files by md5 hash " + md5Hash, ex); //NON-NLS 8981 return Collections.<AbstractFile>emptyList(); 8982 } 8983 acquireSingleUserCaseReadLock(); 8984 Statement s = null; 8985 ResultSet rs = null; 8986 try { 8987 s = connection.createStatement(); 8988 rs = connection.executeQuery(s, "SELECT * FROM tsk_files WHERE " //NON-NLS 8989 + " md5 = '" + md5Hash.toLowerCase() + "' " //NON-NLS 8990 + "AND size > 0"); //NON-NLS 8991 return resultSetToAbstractFiles(rs, connection); 8992 } catch (SQLException ex) { 8993 logger.log(Level.WARNING, "Error querying database.", ex); //NON-NLS 8994 return Collections.<AbstractFile>emptyList(); 8995 } finally { 8996 closeResultSet(rs); 8997 closeStatement(s); 8998 connection.close(); 8999 releaseSingleUserCaseReadLock(); 9000 } 9001 } 9002 9003 /** 9004 * Query all the files to verify if they have an MD5 hash associated with 9005 * them. 9006 * 9007 * @return true if all files have an MD5 hash 9008 */ 9009 public boolean allFilesMd5Hashed() { 9010 CaseDbConnection connection; 9011 try { 9012 connection = connections.getConnection(); 9013 } catch (TskCoreException ex) { 9014 logger.log(Level.SEVERE, "Error checking md5 hashing status", ex); //NON-NLS 9015 return false; 9016 } 9017 boolean allFilesAreHashed = false; 9018 acquireSingleUserCaseReadLock(); 9019 Statement s = null; 9020 ResultSet rs = null; 9021 try { 9022 s = connection.createStatement(); 9023 rs = connection.executeQuery(s, "SELECT COUNT(*) AS count FROM tsk_files " //NON-NLS 9024 + "WHERE dir_type = '" + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + "' " //NON-NLS 9025 + "AND md5 IS NULL " //NON-NLS 9026 + "AND size > '0'"); //NON-NLS 9027 if (rs.next() && rs.getInt("count") == 0) { 9028 allFilesAreHashed = true; 9029 } 9030 } catch (SQLException ex) { 9031 logger.log(Level.WARNING, "Failed to query whether all files have MD5 hashes", ex); //NON-NLS 9032 } finally { 9033 closeResultSet(rs); 9034 closeStatement(s); 9035 connection.close(); 9036 releaseSingleUserCaseReadLock(); 9037 } 9038 return allFilesAreHashed; 9039 } 9040 9041 /** 9042 * Query all the files and counts how many have an MD5 hash. 9043 * 9044 * @return the number of files with an MD5 hash 9045 */ 9046 public int countFilesMd5Hashed() { 9047 CaseDbConnection connection; 9048 try { 9049 connection = connections.getConnection(); 9050 } catch (TskCoreException ex) { 9051 logger.log(Level.SEVERE, "Error getting database connection for hashed files count", ex); //NON-NLS 9052 return 0; 9053 } 9054 int count = 0; 9055 acquireSingleUserCaseReadLock(); 9056 Statement s = null; 9057 ResultSet rs = null; 9058 try { 9059 s = connection.createStatement(); 9060 rs = connection.executeQuery(s, "SELECT COUNT(*) AS count FROM tsk_files " //NON-NLS 9061 + "WHERE md5 IS NOT NULL " //NON-NLS 9062 + "AND size > '0'"); //NON-NLS 9063 if (rs.next()) { 9064 count = rs.getInt("count"); 9065 } 9066 } catch (SQLException ex) { 9067 logger.log(Level.WARNING, "Failed to query for all the files.", ex); //NON-NLS 9068 } finally { 9069 closeResultSet(rs); 9070 closeStatement(s); 9071 connection.close(); 9072 releaseSingleUserCaseReadLock(); 9073 } 9074 return count; 9075 9076 } 9077 9078 /** 9079 * Selects all of the rows from the tag_names table in the case database. 9080 * 9081 * @return A list, possibly empty, of TagName data transfer objects (DTOs) 9082 * for the rows. 9083 * 9084 * @throws TskCoreException 9085 */ 9086 public List<TagName> getAllTagNames() throws TskCoreException { 9087 CaseDbConnection connection = connections.getConnection(); 9088 acquireSingleUserCaseReadLock(); 9089 ResultSet resultSet = null; 9090 try { 9091 // SELECT * FROM tag_names 9092 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_TAG_NAMES); 9093 resultSet = connection.executeQuery(statement); 9094 ArrayList<TagName> tagNames = new ArrayList<TagName>(); 9095 while (resultSet.next()) { 9096 tagNames.add(new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), 9097 resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), 9098 TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")))); //NON-NLS 9099 } 9100 return tagNames; 9101 } catch (SQLException ex) { 9102 throw new TskCoreException("Error selecting rows from tag_names table", ex); 9103 } finally { 9104 closeResultSet(resultSet); 9105 connection.close(); 9106 releaseSingleUserCaseReadLock(); 9107 } 9108 } 9109 9110 /** 9111 * Selects all of the rows from the tag_names table in the case database for 9112 * which there is at least one matching row in the content_tags or 9113 * blackboard_artifact_tags tables. 9114 * 9115 * @return A list, possibly empty, of TagName data transfer objects (DTOs) 9116 * for the rows. 9117 * 9118 * @throws TskCoreException 9119 */ 9120 public List<TagName> getTagNamesInUse() throws TskCoreException { 9121 CaseDbConnection connection = connections.getConnection(); 9122 acquireSingleUserCaseReadLock(); 9123 ResultSet resultSet = null; 9124 try { 9125 // 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) 9126 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_TAG_NAMES_IN_USE); 9127 resultSet = connection.executeQuery(statement); 9128 ArrayList<TagName> tagNames = new ArrayList<TagName>(); 9129 while (resultSet.next()) { 9130 tagNames.add(new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), 9131 resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), 9132 TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")))); //NON-NLS 9133 } 9134 return tagNames; 9135 } catch (SQLException ex) { 9136 throw new TskCoreException("Error selecting rows from tag_names table", ex); 9137 } finally { 9138 closeResultSet(resultSet); 9139 connection.close(); 9140 releaseSingleUserCaseReadLock(); 9141 } 9142 } 9143 9144 /** 9145 * Selects all of the rows from the tag_names table in the case database for 9146 * which there is at least one matching row in the content_tags or 9147 * blackboard_artifact_tags tables, for the given data source object id. 9148 * 9149 * @param dsObjId data source object id 9150 * 9151 * @return A list, possibly empty, of TagName data transfer objects (DTOs) 9152 * for the rows. 9153 * 9154 * @throws TskCoreException 9155 */ 9156 public List<TagName> getTagNamesInUse(long dsObjId) throws TskCoreException { 9157 9158 ArrayList<TagName> tagNames = new ArrayList<TagName>(); 9159 // SELECT * FROM tag_names WHERE tag_name_id IN 9160 // ( SELECT content_tags.tag_name_id as tag_name_id FROM content_tags as content_tags, tsk_files as tsk_files WHERE content_tags.obj_id = tsk_files.obj_id AND tsk_files.data_source_obj_id = ? " 9161 // UNION 9162 // SELECT artifact_tags.tag_name_id as tag_name_id FROM blackboard_artifact_tags as artifact_tags, blackboard_artifacts AS arts WHERE artifact_tags.artifact_id = arts.artifact_id AND arts.data_source_obj_id = ? ) 9163 // ) 9164 CaseDbConnection connection = connections.getConnection(); 9165 acquireSingleUserCaseReadLock(); 9166 ResultSet resultSet = null; 9167 9168 try { 9169 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_TAG_NAMES_IN_USE_BY_DATASOURCE); 9170 statement.setLong(1, dsObjId); 9171 statement.setLong(2, dsObjId); 9172 resultSet = connection.executeQuery(statement); //NON-NLS 9173 while (resultSet.next()) { 9174 tagNames.add(new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), 9175 resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), 9176 TskData.FileKnown.valueOf(resultSet.getByte("knownStatus")))); //NON-NLS 9177 } 9178 return tagNames; 9179 } catch (SQLException ex) { 9180 throw new TskCoreException("Failed to get tag names in use for data source objID : " + dsObjId, ex); 9181 } finally { 9182 closeResultSet(resultSet); 9183 connection.close(); 9184 releaseSingleUserCaseReadLock(); 9185 } 9186 } 9187 9188 /** 9189 * Inserts row into the tags_names table in the case database. 9190 * 9191 * @param displayName The display name for the new tag name. 9192 * @param description The description for the new tag name. 9193 * @param color The HTML color to associate with the new tag name. 9194 * 9195 * @return A TagName data transfer object (DTO) for the new row. 9196 * 9197 * @throws TskCoreException 9198 * @deprecated addOrUpdateTagName should be used this method calls 9199 * addOrUpdateTagName with a default knownStatus value 9200 */ 9201 @Deprecated 9202 public TagName addTagName(String displayName, String description, TagName.HTML_COLOR color) throws TskCoreException { 9203 return addOrUpdateTagName(displayName, description, color, TskData.FileKnown.UNKNOWN); 9204 } 9205 9206 /** 9207 * Inserts row into the tags_names table, or updates the existing row if the 9208 * displayName already exists in the tag_names table in the case database. 9209 * 9210 * @param displayName The display name for the new tag name. 9211 * @param description The description for the new tag name. 9212 * @param color The HTML color to associate with the new tag name. 9213 * @param knownStatus The TskData.FileKnown value to associate with the new 9214 * tag name. 9215 * 9216 * @return A TagName data transfer object (DTO) for the new row. 9217 * 9218 * @throws TskCoreException 9219 */ 9220 public TagName addOrUpdateTagName(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown knownStatus) throws TskCoreException { 9221 CaseDbConnection connection = connections.getConnection(); 9222 acquireSingleUserCaseWriteLock(); 9223 ResultSet resultSet = null; 9224 try { 9225 PreparedStatement statement; 9226 if (dbType == DbType.POSTGRESQL) { 9227 // INSERT INTO tag_names (display_name, description, color, knownStatus) VALUES (?, ?, ?, ?) ON CONFLICT (display_name) DO UPDATE SET description = ?, color = ?, knownStatus = ? 9228 statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_OR_UPDATE_TAG_NAME_POSTGRES, Statement.RETURN_GENERATED_KEYS); 9229 statement.clearParameters(); 9230 statement.setString(5, description); 9231 statement.setString(6, color.getName()); 9232 statement.setByte(7, knownStatus.getFileKnownValue()); 9233 } else { 9234 // WITH new (display_name, description, color, knownStatus) 9235 // AS ( VALUES(?, ?, ?, ?)) INSERT OR REPLACE INTO tag_names 9236 // (tag_name_id, display_name, description, color, knownStatus) 9237 // SELECT old.tag_name_id, new.display_name, new.description, new.color, new.knownStatus 9238 // FROM new LEFT JOIN tag_names AS old ON new.display_name = old.display_name 9239 statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_OR_UPDATE_TAG_NAME_SQLITE, Statement.RETURN_GENERATED_KEYS); 9240 statement.clearParameters(); 9241 } 9242 statement.setString(1, displayName); 9243 statement.setString(2, description); 9244 statement.setString(3, color.getName()); 9245 statement.setByte(4, knownStatus.getFileKnownValue()); 9246 connection.executeUpdate(statement); 9247 resultSet = statement.getGeneratedKeys(); 9248 resultSet.next(); 9249 return new TagName(resultSet.getLong(1), //last_insert_rowid() 9250 displayName, description, color, knownStatus); 9251 } catch (SQLException ex) { 9252 throw new TskCoreException("Error adding row for " + displayName + " tag name to tag_names table", ex); 9253 } finally { 9254 closeResultSet(resultSet); 9255 connection.close(); 9256 releaseSingleUserCaseWriteLock(); 9257 } 9258 } 9259 9260 /** 9261 * Inserts a row into the content_tags table in the case database. 9262 * 9263 * @param content The content to tag. 9264 * @param tagName The name to use for the tag. 9265 * @param comment A comment to store with the tag. 9266 * @param beginByteOffset Designates the beginning of a tagged section. 9267 * @param endByteOffset Designates the end of a tagged section. 9268 * 9269 * @return A ContentTag data transfer object (DTO) for the new row. 9270 * 9271 * @throws TskCoreException 9272 */ 9273 public ContentTag addContentTag(Content content, TagName tagName, String comment, long beginByteOffset, long endByteOffset) throws TskCoreException { 9274 CaseDbConnection connection = connections.getConnection(); 9275 acquireSingleUserCaseWriteLock(); 9276 ResultSet resultSet = null; 9277 try { 9278 Examiner currentExaminer = getCurrentExaminer(); 9279 // INSERT INTO content_tags (obj_id, tag_name_id, comment, begin_byte_offset, end_byte_offset, examiner_id) VALUES (?, ?, ?, ?, ?, ?) 9280 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_CONTENT_TAG, Statement.RETURN_GENERATED_KEYS); 9281 statement.clearParameters(); 9282 statement.setLong(1, content.getId()); 9283 statement.setLong(2, tagName.getId()); 9284 statement.setString(3, comment); 9285 statement.setLong(4, beginByteOffset); 9286 statement.setLong(5, endByteOffset); 9287 statement.setLong(6, currentExaminer.getId()); 9288 connection.executeUpdate(statement); 9289 resultSet = statement.getGeneratedKeys(); 9290 resultSet.next(); 9291 return new ContentTag(resultSet.getLong(1), //last_insert_rowid() 9292 content, tagName, comment, beginByteOffset, endByteOffset, currentExaminer.getLoginName()); 9293 } catch (SQLException ex) { 9294 throw new TskCoreException("Error adding row to content_tags table (obj_id = " + content.getId() + ", tag_name_id = " + tagName.getId() + ")", ex); 9295 } finally { 9296 closeResultSet(resultSet); 9297 connection.close(); 9298 releaseSingleUserCaseWriteLock(); 9299 } 9300 } 9301 9302 /* 9303 * Deletes a row from the content_tags table in the case database. @param 9304 * tag A ContentTag data transfer object (DTO) for the row to delete. 9305 * @throws TskCoreException 9306 */ 9307 public void deleteContentTag(ContentTag tag) throws TskCoreException { 9308 CaseDbConnection connection = connections.getConnection(); 9309 acquireSingleUserCaseWriteLock(); 9310 try { 9311 // DELETE FROM content_tags WHERE tag_id = ? 9312 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.DELETE_CONTENT_TAG); 9313 statement.clearParameters(); 9314 statement.setLong(1, tag.getId()); 9315 connection.executeUpdate(statement); 9316 } catch (SQLException ex) { 9317 throw new TskCoreException("Error deleting row from content_tags table (id = " + tag.getId() + ")", ex); 9318 } finally { 9319 connection.close(); 9320 releaseSingleUserCaseWriteLock(); 9321 } 9322 } 9323 9324 /** 9325 * Selects all of the rows from the content_tags table in the case database. 9326 * 9327 * @return A list, possibly empty, of ContentTag data transfer objects 9328 * (DTOs) for the rows. 9329 * 9330 * @throws TskCoreException 9331 */ 9332 public List<ContentTag> getAllContentTags() throws TskCoreException { 9333 CaseDbConnection connection = connections.getConnection(); 9334 acquireSingleUserCaseReadLock(); 9335 ResultSet resultSet = null; 9336 try { 9337 // SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name 9338 // FROM content_tags 9339 // INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id 9340 // LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id 9341 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_CONTENT_TAGS); 9342 resultSet = connection.executeQuery(statement); 9343 ArrayList<ContentTag> tags = new ArrayList<ContentTag>(); 9344 while (resultSet.next()) { 9345 TagName tagName = new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), 9346 resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), 9347 TskData.FileKnown.valueOf(resultSet.getByte("knownStatus"))); //NON-NLS 9348 Content content = getContentById(resultSet.getLong("obj_id")); //NON-NLS 9349 tags.add(new ContentTag(resultSet.getLong("tag_id"), content, tagName, resultSet.getString("comment"), 9350 resultSet.getLong("begin_byte_offset"), resultSet.getLong("end_byte_offset"), resultSet.getString("login_name"))); //NON-NLS 9351 } 9352 return tags; 9353 } catch (SQLException ex) { 9354 throw new TskCoreException("Error selecting rows from content_tags table", ex); 9355 } finally { 9356 closeResultSet(resultSet); 9357 connection.close(); 9358 releaseSingleUserCaseReadLock(); 9359 } 9360 } 9361 9362 /** 9363 * Gets a count of the rows in the content_tags table in the case database 9364 * with a specified foreign key into the tag_names table. 9365 * 9366 * @param tagName A data transfer object (DTO) for the tag name to match. 9367 * 9368 * @return The count, possibly zero. 9369 * 9370 * @throws TskCoreException 9371 */ 9372 public long getContentTagsCountByTagName(TagName tagName) throws TskCoreException { 9373 if (tagName.getId() == Tag.ID_NOT_SET) { 9374 throw new TskCoreException("TagName object is invalid, id not set"); 9375 } 9376 CaseDbConnection connection = connections.getConnection(); 9377 acquireSingleUserCaseReadLock(); 9378 ResultSet resultSet = null; 9379 try { 9380 // SELECT COUNT(*) AS count FROM content_tags WHERE tag_name_id = ? 9381 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_CONTENT_TAGS_BY_TAG_NAME); 9382 statement.clearParameters(); 9383 statement.setLong(1, tagName.getId()); 9384 resultSet = connection.executeQuery(statement); 9385 if (resultSet.next()) { 9386 return resultSet.getLong("count"); 9387 } else { 9388 throw new TskCoreException("Error getting content_tags row count for tag name (tag_name_id = " + tagName.getId() + ")"); 9389 } 9390 } catch (SQLException ex) { 9391 throw new TskCoreException("Error getting content_tags row count for tag name (tag_name_id = " + tagName.getId() + ")", ex); 9392 } finally { 9393 closeResultSet(resultSet); 9394 connection.close(); 9395 releaseSingleUserCaseReadLock(); 9396 } 9397 } 9398 9399 /** 9400 * Gets content tags count by tag name, for the given data source 9401 * 9402 * @param tagName The representation of the desired tag type in the case 9403 * database, which can be obtained by calling getTagNames 9404 * and/or addTagName. 9405 * 9406 * @param dsObjId data source object id 9407 * 9408 * @return A count of the content tags with the specified tag name, and for 9409 * the given data source 9410 * 9411 * @throws TskCoreException If there is an error getting the tags count from 9412 * the case database. 9413 */ 9414 public long getContentTagsCountByTagName(TagName tagName, long dsObjId) throws TskCoreException { 9415 9416 if (tagName.getId() == Tag.ID_NOT_SET) { 9417 throw new TskCoreException("TagName object is invalid, id not set"); 9418 } 9419 9420 CaseDbConnection connection = connections.getConnection(); 9421 acquireSingleUserCaseReadLock(); 9422 ResultSet resultSet = null; 9423 try { 9424 // "SELECT COUNT(*) AS count FROM content_tags as content_tags, tsk_files as tsk_files WHERE content_tags.obj_id = tsk_files.obj_id" 9425 // + " AND content_tags.tag_name_id = ? " 9426 // + " AND tsk_files.data_source_obj_id = ? " 9427 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_CONTENT_TAGS_BY_TAG_NAME_BY_DATASOURCE); 9428 statement.clearParameters(); 9429 statement.setLong(1, tagName.getId()); 9430 statement.setLong(2, dsObjId); 9431 9432 resultSet = connection.executeQuery(statement); 9433 if (resultSet.next()) { 9434 return resultSet.getLong("count"); 9435 } else { 9436 throw new TskCoreException("Error getting content_tags row count for tag name (tag_name_id = " + tagName.getId() + ")" + " for dsObjId = " + dsObjId); 9437 } 9438 } catch (SQLException ex) { 9439 throw new TskCoreException("Failed to get content_tags row count for tag_name_id = " + tagName.getId() + "data source objID : " + dsObjId, ex); 9440 } finally { 9441 closeResultSet(resultSet); 9442 connection.close(); 9443 releaseSingleUserCaseReadLock(); 9444 } 9445 } 9446 9447 /** 9448 * Selects the rows in the content_tags table in the case database with a 9449 * specified tag id. 9450 * 9451 * @param contentTagID the tag id of the ContentTag to retrieve. 9452 * 9453 * @return The content tag. 9454 * 9455 * @throws TskCoreException 9456 */ 9457 public ContentTag getContentTagByID(long contentTagID) throws TskCoreException { 9458 9459 CaseDbConnection connection = connections.getConnection(); 9460 acquireSingleUserCaseReadLock(); 9461 ResultSet resultSet = null; 9462 ContentTag tag = null; 9463 try { 9464 // SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name 9465 // FROM content_tags 9466 // INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id 9467 // UTER LEFT JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id 9468 // WHERE tag_id = ? 9469 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_CONTENT_TAG_BY_ID); 9470 statement.clearParameters(); 9471 statement.setLong(1, contentTagID); 9472 resultSet = connection.executeQuery(statement); 9473 9474 while (resultSet.next()) { 9475 TagName tagName = new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), 9476 resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), 9477 TskData.FileKnown.valueOf(resultSet.getByte("knownStatus"))); 9478 tag = new ContentTag(resultSet.getLong("tag_id"), getContentById(resultSet.getLong("obj_id")), tagName, 9479 resultSet.getString("comment"), resultSet.getLong("begin_byte_offset"), resultSet.getLong("end_byte_offset"), resultSet.getString("login_name")); 9480 } 9481 resultSet.close(); 9482 9483 } catch (SQLException ex) { 9484 throw new TskCoreException("Error getting content tag with id = " + contentTagID, ex); 9485 } finally { 9486 closeResultSet(resultSet); 9487 connection.close(); 9488 releaseSingleUserCaseReadLock(); 9489 } 9490 return tag; 9491 } 9492 9493 /** 9494 * Selects the rows in the content_tags table in the case database with a 9495 * specified foreign key into the tag_names table. 9496 * 9497 * @param tagName A data transfer object (DTO) for the tag name to match. 9498 * 9499 * @return A list, possibly empty, of ContentTag data transfer objects 9500 * (DTOs) for the rows. 9501 * 9502 * @throws TskCoreException 9503 */ 9504 public List<ContentTag> getContentTagsByTagName(TagName tagName) throws TskCoreException { 9505 if (tagName.getId() == Tag.ID_NOT_SET) { 9506 throw new TskCoreException("TagName object is invalid, id not set"); 9507 } 9508 CaseDbConnection connection = connections.getConnection(); 9509 acquireSingleUserCaseReadLock(); 9510 ResultSet resultSet = null; 9511 try { 9512 // SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tsk_examiners.login_name 9513 // FROM content_tags 9514 // LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id 9515 // WHERE tag_name_id = ? 9516 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_CONTENT_TAGS_BY_TAG_NAME); 9517 statement.clearParameters(); 9518 statement.setLong(1, tagName.getId()); 9519 resultSet = connection.executeQuery(statement); 9520 ArrayList<ContentTag> tags = new ArrayList<ContentTag>(); 9521 while (resultSet.next()) { 9522 ContentTag tag = new ContentTag(resultSet.getLong("tag_id"), getContentById(resultSet.getLong("obj_id")), 9523 tagName, resultSet.getString("comment"), resultSet.getLong("begin_byte_offset"), resultSet.getLong("end_byte_offset"), resultSet.getString("login_name")); //NON-NLS 9524 tags.add(tag); 9525 } 9526 resultSet.close(); 9527 return tags; 9528 } catch (SQLException ex) { 9529 throw new TskCoreException("Error getting content_tags rows (tag_name_id = " + tagName.getId() + ")", ex); 9530 } finally { 9531 closeResultSet(resultSet); 9532 connection.close(); 9533 releaseSingleUserCaseReadLock(); 9534 } 9535 } 9536 9537 /** 9538 * Gets content tags by tag name, for the given data source. 9539 * 9540 * @param tagName The tag name of interest. 9541 * @param dsObjId data source object id 9542 * 9543 * @return A list, possibly empty, of the content tags with the specified 9544 * tag name, and for the given data source. 9545 * 9546 * @throws TskCoreException If there is an error getting the tags from the 9547 * case database. 9548 */ 9549 public List<ContentTag> getContentTagsByTagName(TagName tagName, long dsObjId) throws TskCoreException { 9550 9551 CaseDbConnection connection = connections.getConnection(); 9552 acquireSingleUserCaseReadLock(); 9553 ResultSet resultSet = null; 9554 try { 9555 9556 // SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name 9557 // FROM content_tags as content_tags, tsk_files as tsk_files 9558 // LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id 9559 // WHERE content_tags.obj_id = tsk_files.obj_id 9560 // AND content_tags.tag_name_id = ? 9561 // AND tsk_files.data_source_obj_id = ? 9562 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_CONTENT_TAGS_BY_TAG_NAME_BY_DATASOURCE); 9563 statement.clearParameters(); 9564 statement.setLong(1, tagName.getId()); 9565 statement.setLong(2, dsObjId); 9566 resultSet = connection.executeQuery(statement); 9567 ArrayList<ContentTag> tags = new ArrayList<ContentTag>(); 9568 while (resultSet.next()) { 9569 ContentTag tag = new ContentTag(resultSet.getLong("tag_id"), getContentById(resultSet.getLong("obj_id")), 9570 tagName, resultSet.getString("comment"), resultSet.getLong("begin_byte_offset"), resultSet.getLong("end_byte_offset"), resultSet.getString("login_name")); //NON-NLS 9571 tags.add(tag); 9572 } 9573 resultSet.close(); 9574 return tags; 9575 } catch (SQLException ex) { 9576 throw new TskCoreException("Failed to get content_tags row count for tag_name_id = " + tagName.getId() + " data source objID : " + dsObjId, ex); 9577 } finally { 9578 closeResultSet(resultSet); 9579 connection.close(); 9580 releaseSingleUserCaseReadLock(); 9581 } 9582 } 9583 9584 /** 9585 * Selects the rows in the content_tags table in the case database with a 9586 * specified foreign key into the tsk_objects table. 9587 * 9588 * @param content A data transfer object (DTO) for the content to match. 9589 * 9590 * @return A list, possibly empty, of ContentTag data transfer objects 9591 * (DTOs) for the rows. 9592 * 9593 * @throws TskCoreException 9594 */ 9595 public List<ContentTag> getContentTagsByContent(Content content) throws TskCoreException { 9596 CaseDbConnection connection = connections.getConnection(); 9597 acquireSingleUserCaseReadLock(); 9598 ResultSet resultSet = null; 9599 try { 9600 // SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name 9601 // FROM content_tags 9602 // INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id 9603 // LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id 9604 // WHERE content_tags.obj_id = ? 9605 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_CONTENT_TAGS_BY_CONTENT); 9606 statement.clearParameters(); 9607 statement.setLong(1, content.getId()); 9608 resultSet = connection.executeQuery(statement); 9609 ArrayList<ContentTag> tags = new ArrayList<ContentTag>(); 9610 while (resultSet.next()) { 9611 TagName tagName = new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), 9612 resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), 9613 TskData.FileKnown.valueOf(resultSet.getByte("knownStatus"))); //NON-NLS 9614 ContentTag tag = new ContentTag(resultSet.getLong("tag_id"), content, tagName, 9615 resultSet.getString("comment"), resultSet.getLong("begin_byte_offset"), resultSet.getLong("end_byte_offset"), resultSet.getString("login_name")); //NON-NLS 9616 tags.add(tag); 9617 } 9618 return tags; 9619 } catch (SQLException ex) { 9620 throw new TskCoreException("Error getting content tags data for content (obj_id = " + content.getId() + ")", ex); 9621 } finally { 9622 closeResultSet(resultSet); 9623 connection.close(); 9624 releaseSingleUserCaseReadLock(); 9625 } 9626 } 9627 9628 /** 9629 * Inserts a row into the blackboard_artifact_tags table in the case 9630 * database. 9631 * 9632 * @param artifact The blackboard artifact to tag. 9633 * @param tagName The name to use for the tag. 9634 * @param comment A comment to store with the tag. 9635 * 9636 * @return A BlackboardArtifactTag data transfer object (DTO) for the new 9637 * row. 9638 * 9639 * @throws TskCoreException 9640 */ 9641 public BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName, String comment) throws TskCoreException { 9642 CaseDbConnection connection = connections.getConnection(); 9643 acquireSingleUserCaseWriteLock(); 9644 ResultSet resultSet = null; 9645 try { 9646 Examiner currentExaminer = getCurrentExaminer(); 9647 // "INSERT INTO blackboard_artifact_tags (artifact_id, tag_name_id, comment, examiner_id) VALUES (?, ?, ?, ?)"), //NON-NLS 9648 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_ARTIFACT_TAG, Statement.RETURN_GENERATED_KEYS); 9649 statement.clearParameters(); 9650 statement.setLong(1, artifact.getArtifactID()); 9651 statement.setLong(2, tagName.getId()); 9652 statement.setString(3, comment); 9653 statement.setLong(4, currentExaminer.getId()); 9654 connection.executeUpdate(statement); 9655 resultSet = statement.getGeneratedKeys(); 9656 resultSet.next(); 9657 return new BlackboardArtifactTag(resultSet.getLong(1), //last_insert_rowid() 9658 artifact, getContentById(artifact.getObjectID()), tagName, comment, currentExaminer.getLoginName()); 9659 } catch (SQLException ex) { 9660 throw new TskCoreException("Error adding row to blackboard_artifact_tags table (obj_id = " + artifact.getArtifactID() + ", tag_name_id = " + tagName.getId() + ")", ex); 9661 } finally { 9662 closeResultSet(resultSet); 9663 connection.close(); 9664 releaseSingleUserCaseWriteLock(); 9665 } 9666 } 9667 9668 /* 9669 * Deletes a row from the blackboard_artifact_tags table in the case 9670 * database. @param tag A BlackboardArtifactTag data transfer object (DTO) 9671 * representing the row to delete. @throws TskCoreException 9672 */ 9673 public void deleteBlackboardArtifactTag(BlackboardArtifactTag tag) throws TskCoreException { 9674 CaseDbConnection connection = connections.getConnection(); 9675 acquireSingleUserCaseWriteLock(); 9676 try { 9677 // DELETE FROM blackboard_artifact_tags WHERE tag_id = ? 9678 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.DELETE_ARTIFACT_TAG); 9679 statement.clearParameters(); 9680 statement.setLong(1, tag.getId()); 9681 connection.executeUpdate(statement); 9682 } catch (SQLException ex) { 9683 throw new TskCoreException("Error deleting row from blackboard_artifact_tags table (id = " + tag.getId() + ")", ex); 9684 } finally { 9685 connection.close(); 9686 releaseSingleUserCaseWriteLock(); 9687 } 9688 } 9689 9690 /** 9691 * Selects all of the rows from the blackboard_artifacts_tags table in the 9692 * case database. 9693 * 9694 * @return A list, possibly empty, of BlackboardArtifactTag data transfer 9695 * objects (DTOs) for the rows. 9696 * 9697 * @throws TskCoreException 9698 */ 9699 public List<BlackboardArtifactTag> getAllBlackboardArtifactTags() throws TskCoreException { 9700 CaseDbConnection connection = connections.getConnection(); 9701 acquireSingleUserCaseReadLock(); 9702 ResultSet resultSet = null; 9703 try { 9704 // SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name 9705 // FROM blackboard_artifact_tags 9706 // INNER JOIN tag_names ON blackboard_artifact_tags.tag_name_id = tag_names.tag_name_id 9707 // LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id 9708 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ARTIFACT_TAGS); 9709 resultSet = connection.executeQuery(statement); 9710 ArrayList<BlackboardArtifactTag> tags = new ArrayList<BlackboardArtifactTag>(); 9711 while (resultSet.next()) { 9712 TagName tagName = new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), 9713 resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), 9714 TskData.FileKnown.valueOf(resultSet.getByte("knownStatus"))); //NON-NLS 9715 BlackboardArtifact artifact = getBlackboardArtifact(resultSet.getLong("artifact_id")); //NON-NLS 9716 Content content = getContentById(artifact.getObjectID()); 9717 BlackboardArtifactTag tag = new BlackboardArtifactTag(resultSet.getLong("tag_id"), 9718 artifact, content, tagName, resultSet.getString("comment"), resultSet.getString("login_name")); //NON-NLS 9719 tags.add(tag); 9720 } 9721 return tags; 9722 } catch (SQLException ex) { 9723 throw new TskCoreException("Error selecting rows from blackboard_artifact_tags table", ex); 9724 } finally { 9725 closeResultSet(resultSet); 9726 connection.close(); 9727 releaseSingleUserCaseReadLock(); 9728 } 9729 } 9730 9731 /** 9732 * Gets a count of the rows in the blackboard_artifact_tags table in the 9733 * case database with a specified foreign key into the tag_names table. 9734 * 9735 * @param tagName A data transfer object (DTO) for the tag name to match. 9736 * 9737 * @return The count, possibly zero. 9738 * 9739 * @throws TskCoreException 9740 */ 9741 public long getBlackboardArtifactTagsCountByTagName(TagName tagName) throws TskCoreException { 9742 if (tagName.getId() == Tag.ID_NOT_SET) { 9743 throw new TskCoreException("TagName object is invalid, id not set"); 9744 } 9745 CaseDbConnection connection = connections.getConnection(); 9746 acquireSingleUserCaseReadLock(); 9747 ResultSet resultSet = null; 9748 try { 9749 // SELECT COUNT(*) AS count FROM blackboard_artifact_tags WHERE tag_name_id = ? 9750 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_BY_TAG_NAME); 9751 statement.clearParameters(); 9752 statement.setLong(1, tagName.getId()); 9753 resultSet = connection.executeQuery(statement); 9754 if (resultSet.next()) { 9755 return resultSet.getLong("count"); 9756 } else { 9757 throw new TskCoreException("Error getting blackboard_artifact_tags row count for tag name (tag_name_id = " + tagName.getId() + ")"); 9758 } 9759 } catch (SQLException ex) { 9760 throw new TskCoreException("Error getting blackboard artifact_content_tags row count for tag name (tag_name_id = " + tagName.getId() + ")", ex); 9761 } finally { 9762 closeResultSet(resultSet); 9763 connection.close(); 9764 releaseSingleUserCaseReadLock(); 9765 } 9766 } 9767 9768 /** 9769 * Gets an artifact tags count by tag name, for the given data source. 9770 * 9771 * @param tagName The representation of the desired tag type in the case 9772 * database, which can be obtained by calling getTagNames 9773 * and/or addTagName. 9774 * @param dsObjId data source object id 9775 * 9776 * @return A count of the artifact tags with the specified tag name, for the 9777 * given data source. 9778 * 9779 * @throws TskCoreException If there is an error getting the tags count from 9780 * the case database. 9781 */ 9782 public long getBlackboardArtifactTagsCountByTagName(TagName tagName, long dsObjId) throws TskCoreException { 9783 9784 if (tagName.getId() == Tag.ID_NOT_SET) { 9785 throw new TskCoreException("TagName object is invalid, id not set"); 9786 } 9787 9788 CaseDbConnection connection = connections.getConnection(); 9789 acquireSingleUserCaseReadLock(); 9790 ResultSet resultSet = null; 9791 try { 9792 // "SELECT COUNT(*) AS count FROM blackboard_artifact_tags as artifact_tags, blackboard_artifacts AS arts WHERE artifact_tags.artifact_id = arts.artifact_id" 9793 // + " AND artifact_tags.tag_name_id = ?" 9794 // + " AND arts.data_source_obj_id = ? " 9795 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_BY_TAG_NAME_BY_DATASOURCE); 9796 statement.clearParameters(); 9797 statement.setLong(1, tagName.getId()); 9798 statement.setLong(2, dsObjId); 9799 resultSet = connection.executeQuery(statement); 9800 if (resultSet.next()) { 9801 return resultSet.getLong("count"); 9802 } else { 9803 throw new TskCoreException("Error getting blackboard_artifact_tags row count for tag name (tag_name_id = " + tagName.getId() + ")" + " for dsObjId = " + dsObjId); 9804 } 9805 } catch (SQLException ex) { 9806 throw new TskCoreException("Failed to get blackboard_artifact_tags row count for tag_name_id = " + tagName.getId() + "data source objID : " + dsObjId, ex); 9807 } finally { 9808 closeResultSet(resultSet); 9809 connection.close(); 9810 releaseSingleUserCaseReadLock(); 9811 } 9812 } 9813 9814 /** 9815 * Selects the rows in the blackboard_artifacts_tags table in the case 9816 * database with a specified foreign key into the tag_names table. 9817 * 9818 * @param tagName A data transfer object (DTO) for the tag name to match. 9819 * 9820 * @return A list, possibly empty, of BlackboardArtifactTag data transfer 9821 * objects (DTOs) for the rows. 9822 * 9823 * @throws TskCoreException 9824 */ 9825 public List<BlackboardArtifactTag> getBlackboardArtifactTagsByTagName(TagName tagName) throws TskCoreException { 9826 if (tagName.getId() == Tag.ID_NOT_SET) { 9827 throw new TskCoreException("TagName object is invalid, id not set"); 9828 } 9829 CaseDbConnection connection = connections.getConnection(); 9830 acquireSingleUserCaseReadLock(); 9831 ResultSet resultSet = null; 9832 try { 9833 // SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tsk_examiners.login_name 9834 // FROM blackboard_artifact_tags 9835 // LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id 9836 // WHERE tag_name_id = ? 9837 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ARTIFACT_TAGS_BY_TAG_NAME); 9838 statement.clearParameters(); 9839 statement.setLong(1, tagName.getId()); 9840 resultSet = connection.executeQuery(statement); 9841 ArrayList<BlackboardArtifactTag> tags = new ArrayList<BlackboardArtifactTag>(); 9842 while (resultSet.next()) { 9843 BlackboardArtifact artifact = getBlackboardArtifact(resultSet.getLong("artifact_id")); //NON-NLS 9844 Content content = getContentById(artifact.getObjectID()); 9845 BlackboardArtifactTag tag = new BlackboardArtifactTag(resultSet.getLong("tag_id"), 9846 artifact, content, tagName, resultSet.getString("comment"), resultSet.getString("login_name")); //NON-NLS 9847 tags.add(tag); 9848 } 9849 return tags; 9850 } catch (SQLException ex) { 9851 throw new TskCoreException("Error getting blackboard artifact tags data (tag_name_id = " + tagName.getId() + ")", ex); 9852 } finally { 9853 closeResultSet(resultSet); 9854 connection.close(); 9855 releaseSingleUserCaseReadLock(); 9856 } 9857 } 9858 9859 /** 9860 * Gets artifact tags by tag name, for specified data source. 9861 * 9862 * @param tagName The representation of the desired tag type in the case 9863 * database, which can be obtained by calling getTagNames 9864 * and/or addTagName. 9865 * @param dsObjId data source object id 9866 * 9867 * @return A list, possibly empty, of the artifact tags with the specified 9868 * tag name, for the specified data source. 9869 * 9870 * @throws TskCoreException If there is an error getting the tags from the 9871 * case database. 9872 */ 9873 public List<BlackboardArtifactTag> getBlackboardArtifactTagsByTagName(TagName tagName, long dsObjId) throws TskCoreException { 9874 9875 if (tagName.getId() == Tag.ID_NOT_SET) { 9876 throw new TskCoreException("TagName object is invalid, id not set"); 9877 } 9878 9879 CaseDbConnection connection = connections.getConnection(); 9880 acquireSingleUserCaseReadLock(); 9881 ResultSet resultSet = null; 9882 try { 9883 // SELECT artifact_tags.tag_id, artifact_tags.artifact_id, artifact_tags.tag_name_id, artifact_tags.comment, arts.obj_id, arts.artifact_obj_id, arts.data_source_obj_id, arts.artifact_type_id, arts.review_status_id, tsk_examiners.login_name 9884 // FROM blackboard_artifact_tags as artifact_tags, blackboard_artifacts AS arts 9885 // LEFT OUTER JOIN tsk_examiners ON artifact_tags.examiner_id = tsk_examiners.examiner_id 9886 // WHERE artifact_tags.artifact_id = arts.artifact_id 9887 // AND artifact_tags.tag_name_id = ? 9888 // AND arts.data_source_obj_id = ? 9889 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ARTIFACT_TAGS_BY_TAG_NAME_BY_DATASOURCE); 9890 statement.clearParameters(); 9891 statement.setLong(1, tagName.getId()); 9892 statement.setLong(2, dsObjId); 9893 resultSet = connection.executeQuery(statement); 9894 ArrayList<BlackboardArtifactTag> tags = new ArrayList<BlackboardArtifactTag>(); 9895 while (resultSet.next()) { 9896 BlackboardArtifact artifact = getBlackboardArtifact(resultSet.getLong("artifact_id")); //NON-NLS 9897 Content content = getContentById(artifact.getObjectID()); 9898 BlackboardArtifactTag tag = new BlackboardArtifactTag(resultSet.getLong("tag_id"), 9899 artifact, content, tagName, resultSet.getString("comment"), resultSet.getString("login_name")); //NON-NLS 9900 tags.add(tag); 9901 } 9902 return tags; 9903 } catch (SQLException ex) { 9904 throw new TskCoreException("Failed to get blackboard_artifact_tags row count for tag_name_id = " + tagName.getId() + "data source objID : " + dsObjId, ex); 9905 } finally { 9906 closeResultSet(resultSet); 9907 connection.close(); 9908 releaseSingleUserCaseReadLock(); 9909 } 9910 9911 } 9912 9913 /** 9914 * Selects the row in the blackboard artifact tags table in the case 9915 * database with a specified tag id. 9916 * 9917 * @param artifactTagID the tag id of the BlackboardArtifactTag to retrieve. 9918 * 9919 * @return the BlackBoardArtifact Tag with the given tag id, or null if no 9920 * such tag could be found 9921 * 9922 * @throws TskCoreException 9923 */ 9924 public BlackboardArtifactTag getBlackboardArtifactTagByID(long artifactTagID) throws TskCoreException { 9925 9926 CaseDbConnection connection = connections.getConnection(); 9927 acquireSingleUserCaseReadLock(); 9928 ResultSet resultSet = null; 9929 BlackboardArtifactTag tag = null; 9930 try { 9931 //SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name 9932 // FROM blackboard_artifact_tags 9933 // INNER JOIN tag_names ON blackboard_artifact_tags.tag_name_id = tag_names.tag_name_id 9934 // LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id 9935 // WHERE blackboard_artifact_tags.tag_id = ? 9936 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ARTIFACT_TAG_BY_ID); 9937 statement.clearParameters(); 9938 statement.setLong(1, artifactTagID); 9939 resultSet = connection.executeQuery(statement); 9940 9941 while (resultSet.next()) { 9942 TagName tagName = new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), 9943 resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), 9944 TskData.FileKnown.valueOf(resultSet.getByte("knownStatus"))); 9945 BlackboardArtifact artifact = getBlackboardArtifact(resultSet.getLong("artifact_id")); //NON-NLS 9946 Content content = getContentById(artifact.getObjectID()); 9947 tag = new BlackboardArtifactTag(resultSet.getLong("tag_id"), 9948 artifact, content, tagName, resultSet.getString("comment"), resultSet.getString("login_name")); 9949 } 9950 resultSet.close(); 9951 9952 } catch (SQLException ex) { 9953 throw new TskCoreException("Error getting blackboard artifact tag with id = " + artifactTagID, ex); 9954 } finally { 9955 closeResultSet(resultSet); 9956 connection.close(); 9957 releaseSingleUserCaseReadLock(); 9958 } 9959 return tag; 9960 } 9961 9962 /** 9963 * Selects the rows in the blackboard_artifacts_tags table in the case 9964 * database with a specified foreign key into the blackboard_artifacts 9965 * table. 9966 * 9967 * @param artifact A data transfer object (DTO) for the artifact to match. 9968 * 9969 * @return A list, possibly empty, of BlackboardArtifactTag data transfer 9970 * objects (DTOs) for the rows. 9971 * 9972 * @throws TskCoreException 9973 */ 9974 public List<BlackboardArtifactTag> getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact) throws TskCoreException { 9975 CaseDbConnection connection = connections.getConnection(); 9976 acquireSingleUserCaseReadLock(); 9977 ResultSet resultSet = null; 9978 try { 9979 // SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name 9980 // FROM blackboard_artifact_tags 9981 // INNER JOIN tag_names ON blackboard_artifact_tags.tag_name_id = tag_names.tag_name_id 9982 // LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id 9983 // WHERE blackboard_artifact_tags.artifact_id = ? 9984 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ARTIFACT_TAGS_BY_ARTIFACT); 9985 statement.clearParameters(); 9986 statement.setLong(1, artifact.getArtifactID()); 9987 resultSet = connection.executeQuery(statement); 9988 ArrayList<BlackboardArtifactTag> tags = new ArrayList<BlackboardArtifactTag>(); 9989 while (resultSet.next()) { 9990 TagName tagName = new TagName(resultSet.getLong("tag_name_id"), resultSet.getString("display_name"), 9991 resultSet.getString("description"), TagName.HTML_COLOR.getColorByName(resultSet.getString("color")), 9992 TskData.FileKnown.valueOf(resultSet.getByte("knownStatus"))); //NON-NLS 9993 Content content = getContentById(artifact.getObjectID()); 9994 BlackboardArtifactTag tag = new BlackboardArtifactTag(resultSet.getLong("tag_id"), 9995 artifact, content, tagName, resultSet.getString("comment"), resultSet.getString("login_name")); //NON-NLS 9996 tags.add(tag); 9997 } 9998 return tags; 9999 } catch (SQLException ex) { 10000 throw new TskCoreException("Error getting blackboard artifact tags data (artifact_id = " + artifact.getArtifactID() + ")", ex); 10001 } finally { 10002 closeResultSet(resultSet); 10003 connection.close(); 10004 releaseSingleUserCaseReadLock(); 10005 } 10006 } 10007 10008 /** 10009 * Change the path for an image in the database. 10010 * 10011 * @param newPath New path to the image 10012 * @param objectId Data source ID of the image 10013 * 10014 * @throws TskCoreException 10015 */ 10016 public void updateImagePath(String newPath, long objectId) throws TskCoreException { 10017 CaseDbConnection connection = connections.getConnection(); 10018 acquireSingleUserCaseWriteLock(); 10019 try { 10020 // UPDATE tsk_image_names SET name = ? WHERE obj_id = ? 10021 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.UPDATE_IMAGE_PATH); 10022 statement.clearParameters(); 10023 statement.setString(1, newPath); 10024 statement.setLong(2, objectId); 10025 connection.executeUpdate(statement); 10026 } catch (SQLException ex) { 10027 throw new TskCoreException("Error updating image path in database for object " + objectId, ex); 10028 } finally { 10029 connection.close(); 10030 releaseSingleUserCaseWriteLock(); 10031 } 10032 } 10033 10034 /** 10035 * Inserts a row into the reports table in the case database. 10036 * 10037 * @param localPath The path of the report file, must be in the 10038 * database directory (case directory in Autopsy) or 10039 * one of its subdirectories. 10040 * @param sourceModuleName The name of the module that created the report. 10041 * @param reportName The report name. 10042 * 10043 * @return A Report object for the new row. 10044 * 10045 * @throws TskCoreException 10046 */ 10047 public Report addReport(String localPath, String sourceModuleName, String reportName) throws TskCoreException { 10048 return addReport(localPath, sourceModuleName, reportName, null); 10049 } 10050 10051 /** 10052 * Inserts a row into the reports table in the case database. 10053 * 10054 * @param localPath The path of the report file, must be in the 10055 * database directory (case directory in Autopsy) or 10056 * one of its subdirectories. 10057 * @param sourceModuleName The name of the module that created the report. 10058 * @param reportName The report name. 10059 * @param parent The Content from which the report was created, if 10060 * available. 10061 * 10062 * @return A Report object for the new row. 10063 * 10064 * @throws TskCoreException 10065 */ 10066 public Report addReport(String localPath, String sourceModuleName, String reportName, Content parent) throws TskCoreException { 10067 // Make sure the local path of the report is in the database directory 10068 // or one of its subdirectories. 10069 String relativePath = ""; //NON-NLS 10070 long createTime = 0; 10071 String localPathLower = localPath.toLowerCase(); 10072 10073 if (localPathLower.startsWith("http")) { 10074 relativePath = localPathLower; 10075 createTime = System.currentTimeMillis() / 1000; 10076 } else { 10077 /* 10078 * Note: The following call to .relativize() may be dangerous in 10079 * case-sensitive operating systems and should be looked at. For 10080 * now, we are simply relativizing the paths as all lower case, then 10081 * using the length of the result to pull out the appropriate number 10082 * of characters from the localPath String. 10083 */ 10084 try { 10085 String casePathLower = getDbDirPath().toLowerCase(); 10086 int length = new File(casePathLower).toURI().relativize(new File(localPathLower).toURI()).getPath().length(); 10087 relativePath = new File(localPath.substring(localPathLower.length() - length)).getPath(); 10088 } catch (IllegalArgumentException ex) { 10089 String errorMessage = String.format("Local path %s not in the database directory or one of its subdirectories", localPath); 10090 throw new TskCoreException(errorMessage, ex); 10091 } 10092 try { 10093 // get its file time 10094 java.io.File tempFile = new java.io.File(localPath); 10095 // Convert to UNIX epoch (seconds, not milliseconds). 10096 createTime = tempFile.lastModified() / 1000; 10097 } catch (Exception ex) { 10098 throw new TskCoreException("Could not get create time for report at " + localPath, ex); 10099 } 10100 } 10101 10102 // Write the report data to the database. 10103 CaseDbConnection connection = connections.getConnection(); 10104 acquireSingleUserCaseWriteLock(); 10105 ResultSet resultSet = null; 10106 try { 10107 // Insert a row for the report into the tsk_objects table. 10108 // INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?) 10109 long parentObjId = 0; 10110 if (parent != null) { 10111 parentObjId = parent.getId(); 10112 } 10113 long objectId = addObject(parentObjId, TskData.ObjectType.REPORT.getObjectType(), connection); 10114 10115 // INSERT INTO reports (obj_id, path, crtime, src_module_name, display_name) VALUES (?, ?, ?, ?, ?) 10116 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_REPORT); 10117 statement.clearParameters(); 10118 statement.setLong(1, objectId); 10119 statement.setString(2, relativePath); 10120 statement.setLong(3, createTime); 10121 statement.setString(4, sourceModuleName); 10122 statement.setString(5, reportName); 10123 connection.executeUpdate(statement); 10124 return new Report(this, objectId, localPath, createTime, sourceModuleName, reportName, parent); 10125 } catch (SQLException ex) { 10126 throw new TskCoreException("Error adding report " + localPath + " to reports table", ex); 10127 } finally { 10128 closeResultSet(resultSet); 10129 connection.close(); 10130 releaseSingleUserCaseWriteLock(); 10131 } 10132 } 10133 10134 /** 10135 * Selects all of the rows from the reports table in the case database. 10136 * 10137 * @return A list, possibly empty, of Report data transfer objects (DTOs) 10138 * for the rows. 10139 * 10140 * @throws TskCoreException 10141 */ 10142 public List<Report> getAllReports() throws TskCoreException { 10143 CaseDbConnection connection = connections.getConnection(); 10144 acquireSingleUserCaseReadLock(); 10145 ResultSet resultSet = null; 10146 ResultSet parentResultSet = null; 10147 PreparedStatement statement = null; 10148 Statement parentStatement = null; 10149 try { 10150 // SELECT * FROM reports 10151 statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_REPORTS); 10152 parentStatement = connection.createStatement(); 10153 resultSet = connection.executeQuery(statement); 10154 ArrayList<Report> reports = new ArrayList<Report>(); 10155 while (resultSet.next()) { 10156 String localpath = resultSet.getString("path"); 10157 if (localpath.toLowerCase().startsWith("http") == false) { 10158 // make path absolute 10159 localpath = Paths.get(getDbDirPath(), localpath).normalize().toString(); //NON-NLS 10160 } 10161 10162 // get the report parent 10163 Content parent = null; 10164 long reportId = resultSet.getLong("obj_id"); // NON-NLS 10165 String parentQuery = String.format("SELECT * FROM tsk_objects WHERE obj_id = %s;", reportId); 10166 parentResultSet = parentStatement.executeQuery(parentQuery); 10167 if (parentResultSet.next()) { 10168 long parentId = parentResultSet.getLong("par_obj_id"); // NON-NLS 10169 parent = this.getContentById(parentId); 10170 } 10171 parentResultSet.close(); 10172 10173 reports.add(new Report(this, 10174 reportId, 10175 localpath, 10176 resultSet.getLong("crtime"), //NON-NLS 10177 resultSet.getString("src_module_name"), //NON-NLS 10178 resultSet.getString("report_name"), 10179 parent)); //NON-NLS 10180 } 10181 return reports; 10182 } catch (SQLException ex) { 10183 throw new TskCoreException("Error querying reports table", ex); 10184 } finally { 10185 closeResultSet(resultSet); 10186 closeResultSet(parentResultSet); 10187 closeStatement(statement); 10188 closeStatement(parentStatement); 10189 10190 connection.close(); 10191 releaseSingleUserCaseReadLock(); 10192 } 10193 } 10194 10195 /** 10196 * Get a Report object for the given id. 10197 * 10198 * @param id 10199 * 10200 * @return A new Report object for the given id. 10201 * 10202 * @throws TskCoreException 10203 */ 10204 public Report getReportById(long id) throws TskCoreException { 10205 CaseDbConnection connection = connections.getConnection(); 10206 acquireSingleUserCaseReadLock(); 10207 PreparedStatement statement = null; 10208 Statement parentStatement = null; 10209 ResultSet resultSet = null; 10210 ResultSet parentResultSet = null; 10211 Report report = null; 10212 try { 10213 // SELECT * FROM reports WHERE obj_id = ? 10214 statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_REPORT_BY_ID); 10215 parentStatement = connection.createStatement(); 10216 statement.clearParameters(); 10217 statement.setLong(1, id); 10218 resultSet = connection.executeQuery(statement); 10219 10220 if (resultSet.next()) { 10221 // get the report parent 10222 Content parent = null; 10223 String parentQuery = String.format("SELECT * FROM tsk_objects WHERE obj_id = %s;", id); 10224 parentResultSet = parentStatement.executeQuery(parentQuery); 10225 if (parentResultSet.next()) { 10226 long parentId = parentResultSet.getLong("par_obj_id"); // NON-NLS 10227 parent = this.getContentById(parentId); 10228 } 10229 10230 report = new Report(this, resultSet.getLong("obj_id"), //NON-NLS 10231 Paths.get(getDbDirPath(), resultSet.getString("path")).normalize().toString(), //NON-NLS 10232 resultSet.getLong("crtime"), //NON-NLS 10233 resultSet.getString("src_module_name"), //NON-NLS 10234 resultSet.getString("report_name"), 10235 parent); //NON-NLS 10236 } else { 10237 throw new TskCoreException("No report found for id: " + id); 10238 } 10239 } catch (SQLException ex) { 10240 throw new TskCoreException("Error querying reports table for id: " + id, ex); 10241 } finally { 10242 closeResultSet(resultSet); 10243 closeResultSet(parentResultSet); 10244 closeStatement(statement); 10245 closeStatement(parentStatement); 10246 connection.close(); 10247 releaseSingleUserCaseReadLock(); 10248 } 10249 10250 return report; 10251 } 10252 10253 /** 10254 * Deletes a row from the reports table in the case database. 10255 * 10256 * @param report A Report data transfer object (DTO) for the row to delete. 10257 * 10258 * @throws TskCoreException 10259 */ 10260 public void deleteReport(Report report) throws TskCoreException { 10261 CaseDbConnection connection = connections.getConnection(); 10262 acquireSingleUserCaseWriteLock(); 10263 try { 10264 // DELETE FROM reports WHERE reports.obj_id = ? 10265 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.DELETE_REPORT); 10266 statement.setLong(1, report.getId()); 10267 connection.executeUpdate(statement); 10268 } catch (SQLException ex) { 10269 throw new TskCoreException("Error querying reports table", ex); 10270 } finally { 10271 releaseSingleUserCaseWriteLock(); 10272 } 10273 } 10274 10275 static void closeResultSet(ResultSet resultSet) { 10276 if (resultSet != null) { 10277 try { 10278 resultSet.close(); 10279 } catch (SQLException ex) { 10280 logger.log(Level.SEVERE, "Error closing ResultSet", ex); //NON-NLS 10281 } 10282 } 10283 } 10284 10285 static void closeStatement(Statement statement) { 10286 if (statement != null) { 10287 try { 10288 statement.close(); 10289 } catch (SQLException ex) { 10290 logger.log(Level.SEVERE, "Error closing Statement", ex); //NON-NLS 10291 10292 } 10293 } 10294 } 10295 10296 /** 10297 * Sets the end date for the given ingest job 10298 * 10299 * @param ingestJobId The ingest job to set the end date for 10300 * @param endDateTime The end date 10301 * 10302 * @throws TskCoreException If inserting into the database fails 10303 */ 10304 void setIngestJobEndDateTime(long ingestJobId, long endDateTime) throws TskCoreException { 10305 CaseDbConnection connection = connections.getConnection(); 10306 acquireSingleUserCaseWriteLock(); 10307 try { 10308 Statement statement = connection.createStatement(); 10309 statement.executeUpdate("UPDATE ingest_jobs SET end_date_time=" + endDateTime + " WHERE ingest_job_id=" + ingestJobId + ";"); 10310 } catch (SQLException ex) { 10311 throw new TskCoreException("Error updating the end date (ingest_job_id = " + ingestJobId + ".", ex); 10312 } finally { 10313 connection.close(); 10314 releaseSingleUserCaseWriteLock(); 10315 } 10316 } 10317 10318 void setIngestJobStatus(long ingestJobId, IngestJobStatusType status) throws TskCoreException { 10319 CaseDbConnection connection = connections.getConnection(); 10320 acquireSingleUserCaseWriteLock(); 10321 try { 10322 Statement statement = connection.createStatement(); 10323 statement.executeUpdate("UPDATE ingest_jobs SET status_id=" + status.ordinal() + " WHERE ingest_job_id=" + ingestJobId + ";"); 10324 } catch (SQLException ex) { 10325 throw new TskCoreException("Error ingest job status (ingest_job_id = " + ingestJobId + ".", ex); 10326 } finally { 10327 connection.close(); 10328 releaseSingleUserCaseWriteLock(); 10329 } 10330 } 10331 10332 /** 10333 * 10334 * @param dataSource The datasource the ingest job is being run on 10335 * @param hostName The name of the host 10336 * @param ingestModules The ingest modules being run during the ingest job. 10337 * Should be in pipeline order. 10338 * @param jobStart The time the job started 10339 * @param jobEnd The time the job ended 10340 * @param status The ingest job status 10341 * @param settingsDir The directory of the job's settings 10342 * 10343 * @return An information object representing the ingest job added to the 10344 * database. 10345 * 10346 * @throws TskCoreException If adding the job to the database fails. 10347 */ 10348 public final IngestJobInfo addIngestJob(Content dataSource, String hostName, List<IngestModuleInfo> ingestModules, Date jobStart, Date jobEnd, IngestJobStatusType status, String settingsDir) throws TskCoreException { 10349 CaseDbConnection connection = connections.getConnection(); 10350 acquireSingleUserCaseWriteLock(); 10351 ResultSet resultSet = null; 10352 Statement statement; 10353 try { 10354 connection.beginTransaction(); 10355 statement = connection.createStatement(); 10356 PreparedStatement insertStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_INGEST_JOB, Statement.RETURN_GENERATED_KEYS); 10357 insertStatement.setLong(1, dataSource.getId()); 10358 insertStatement.setString(2, hostName); 10359 insertStatement.setLong(3, jobStart.getTime()); 10360 insertStatement.setLong(4, jobEnd.getTime()); 10361 insertStatement.setInt(5, status.ordinal()); 10362 insertStatement.setString(6, settingsDir); 10363 connection.executeUpdate(insertStatement); 10364 resultSet = insertStatement.getGeneratedKeys(); 10365 resultSet.next(); 10366 long id = resultSet.getLong(1); //last_insert_rowid() 10367 for (int i = 0; i < ingestModules.size(); i++) { 10368 IngestModuleInfo ingestModule = ingestModules.get(i); 10369 statement.executeUpdate("INSERT INTO ingest_job_modules (ingest_job_id, ingest_module_id, pipeline_position) " 10370 + "VALUES (" + id + ", " + ingestModule.getIngestModuleId() + ", " + i + ");"); 10371 } 10372 resultSet.close(); 10373 resultSet = null; 10374 connection.commitTransaction(); 10375 return new IngestJobInfo(id, dataSource.getId(), hostName, jobStart, "", ingestModules, this); 10376 } catch (SQLException ex) { 10377 connection.rollbackTransaction(); 10378 throw new TskCoreException("Error adding the ingest job.", ex); 10379 } finally { 10380 closeResultSet(resultSet); 10381 connection.close(); 10382 releaseSingleUserCaseWriteLock(); 10383 } 10384 } 10385 10386 /** 10387 * Adds the given ingest module to the database. 10388 * 10389 * @param displayName The display name of the module 10390 * @param factoryClassName The factory class name of the module. 10391 * @param type The type of the module. 10392 * @param version The version of the module. 10393 * 10394 * @return An ingest module info object representing the module added to the 10395 * db. 10396 * 10397 * @throws TskCoreException When the ingest module cannot be added. 10398 */ 10399 public final IngestModuleInfo addIngestModule(String displayName, String factoryClassName, IngestModuleType type, String version) throws TskCoreException { 10400 CaseDbConnection connection = connections.getConnection(); 10401 ResultSet resultSet = null; 10402 Statement statement = null; 10403 String uniqueName = factoryClassName + "-" + displayName + "-" + type.toString() + "-" + version; 10404 acquireSingleUserCaseWriteLock(); 10405 try { 10406 statement = connection.createStatement(); 10407 resultSet = statement.executeQuery("SELECT * FROM ingest_modules WHERE unique_name = '" + uniqueName + "'"); 10408 if (!resultSet.next()) { 10409 resultSet.close(); 10410 resultSet = null; 10411 PreparedStatement insertStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_INGEST_MODULE, Statement.RETURN_GENERATED_KEYS); 10412 insertStatement.setString(1, displayName); 10413 insertStatement.setString(2, uniqueName); 10414 insertStatement.setInt(3, type.ordinal()); 10415 insertStatement.setString(4, version); 10416 connection.executeUpdate(insertStatement); 10417 resultSet = statement.getGeneratedKeys(); 10418 resultSet.next(); 10419 long id = resultSet.getLong(1); //last_insert_rowid() 10420 resultSet.close(); 10421 resultSet = null; 10422 return new IngestModuleInfo(id, displayName, uniqueName, type, version); 10423 } else { 10424 return new IngestModuleInfo(resultSet.getInt("ingest_module_id"), resultSet.getString("display_name"), 10425 resultSet.getString("unique_name"), IngestModuleType.fromID(resultSet.getInt("type_id")), resultSet.getString("version")); 10426 } 10427 } catch (SQLException ex) { 10428 try { 10429 closeStatement(statement); 10430 statement = connection.createStatement(); 10431 resultSet = statement.executeQuery("SELECT * FROM ingest_modules WHERE unique_name = '" + uniqueName + "'"); 10432 if (resultSet.next()) { 10433 return new IngestModuleInfo(resultSet.getInt("ingest_module_id"), resultSet.getString("display_name"), 10434 uniqueName, IngestModuleType.fromID(resultSet.getInt("type_id")), resultSet.getString("version")); 10435 } else { 10436 throw new TskCoreException("Couldn't add new module to database.", ex); 10437 } 10438 } catch (SQLException ex1) { 10439 throw new TskCoreException("Couldn't add new module to database.", ex1); 10440 } 10441 } finally { 10442 closeResultSet(resultSet); 10443 closeStatement(statement); 10444 connection.close(); 10445 releaseSingleUserCaseWriteLock(); 10446 } 10447 } 10448 10449 /** 10450 * Gets all of the ingest jobs that have been run. 10451 * 10452 * @return The information about the ingest jobs that have been run 10453 * 10454 * @throws TskCoreException If there is a problem getting the ingest jobs 10455 */ 10456 public final List<IngestJobInfo> getIngestJobs() throws TskCoreException { 10457 CaseDbConnection connection = connections.getConnection(); 10458 ResultSet resultSet = null; 10459 Statement statement = null; 10460 List<IngestJobInfo> ingestJobs = new ArrayList<IngestJobInfo>(); 10461 acquireSingleUserCaseReadLock(); 10462 try { 10463 statement = connection.createStatement(); 10464 resultSet = statement.executeQuery("SELECT * FROM ingest_jobs"); 10465 while (resultSet.next()) { 10466 ingestJobs.add(new IngestJobInfo(resultSet.getInt("ingest_job_id"), resultSet.getLong("obj_id"), 10467 resultSet.getString("host_name"), new Date(resultSet.getLong("start_date_time")), 10468 new Date(resultSet.getLong("end_date_time")), IngestJobStatusType.fromID(resultSet.getInt("status_id")), 10469 resultSet.getString("settings_dir"), this.getIngestModules(resultSet.getInt("ingest_job_id"), connection), this)); 10470 } 10471 return ingestJobs; 10472 } catch (SQLException ex) { 10473 throw new TskCoreException("Couldn't get the ingest jobs.", ex); 10474 } finally { 10475 closeResultSet(resultSet); 10476 closeStatement(statement); 10477 connection.close(); 10478 releaseSingleUserCaseReadLock(); 10479 } 10480 } 10481 10482 /** 10483 * Gets the ingest modules associated with the ingest job 10484 * 10485 * @param ingestJobId The id of the ingest job to get ingest modules for 10486 * @param connection The database connection 10487 * 10488 * @return The ingest modules of the job 10489 * 10490 * @throws SQLException If it fails to get the modules from the db. 10491 */ 10492 private List<IngestModuleInfo> getIngestModules(int ingestJobId, CaseDbConnection connection) throws SQLException { 10493 ResultSet resultSet = null; 10494 Statement statement = null; 10495 List<IngestModuleInfo> ingestModules = new ArrayList<IngestModuleInfo>(); 10496 acquireSingleUserCaseReadLock(); 10497 try { 10498 statement = connection.createStatement(); 10499 resultSet = statement.executeQuery("SELECT ingest_job_modules.ingest_module_id AS ingest_module_id, " 10500 + "ingest_job_modules.pipeline_position AS pipeline_position, " 10501 + "ingest_modules.display_name AS display_name, ingest_modules.unique_name AS unique_name, " 10502 + "ingest_modules.type_id AS type_id, ingest_modules.version AS version " 10503 + "FROM ingest_job_modules, ingest_modules " 10504 + "WHERE ingest_job_modules.ingest_job_id = " + ingestJobId + " " 10505 + "AND ingest_modules.ingest_module_id = ingest_job_modules.ingest_module_id " 10506 + "ORDER BY (ingest_job_modules.pipeline_position);"); 10507 while (resultSet.next()) { 10508 ingestModules.add(new IngestModuleInfo(resultSet.getInt("ingest_module_id"), resultSet.getString("display_name"), 10509 resultSet.getString("unique_name"), IngestModuleType.fromID(resultSet.getInt("type_id")), resultSet.getString("version"))); 10510 } 10511 return ingestModules; 10512 } finally { 10513 closeResultSet(resultSet); 10514 closeStatement(statement); 10515 releaseSingleUserCaseReadLock(); 10516 10517 } 10518 } 10519 10520 /** 10521 * Stores a pair of object ID and its type 10522 */ 10523 static class ObjectInfo { 10524 10525 private long id; 10526 private TskData.ObjectType type; 10527 10528 ObjectInfo(long id, ObjectType type) { 10529 this.id = id; 10530 this.type = type; 10531 } 10532 10533 long getId() { 10534 return id; 10535 } 10536 10537 TskData.ObjectType getType() { 10538 return type; 10539 } 10540 } 10541 10542 private interface DbCommand { 10543 10544 void execute() throws SQLException; 10545 } 10546 10547 private enum PREPARED_STATEMENT { 10548 10549 SELECT_ARTIFACTS_BY_TYPE("SELECT artifact_id, obj_id FROM blackboard_artifacts " //NON-NLS 10550 + "WHERE artifact_type_id = ?"), //NON-NLS 10551 COUNT_ARTIFACTS_OF_TYPE("SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE artifact_type_id = ? AND review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID()), //NON-NLS 10552 COUNT_ARTIFACTS_FROM_SOURCE("SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE obj_id = ? AND review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID()), //NON-NLS 10553 COUNT_ARTIFACTS_BY_SOURCE_AND_TYPE("SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE obj_id = ? AND artifact_type_id = ? AND review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID()), //NON-NLS 10554 SELECT_FILES_BY_PARENT("SELECT tsk_files.* " //NON-NLS 10555 + "FROM tsk_objects INNER JOIN tsk_files " //NON-NLS 10556 + "ON tsk_objects.obj_id=tsk_files.obj_id " //NON-NLS 10557 + "WHERE (tsk_objects.par_obj_id = ? ) " //NON-NLS 10558 + "ORDER BY tsk_files.meta_type DESC, LOWER(tsk_files.name)"), //NON-NLS 10559 SELECT_FILES_BY_PARENT_AND_TYPE("SELECT tsk_files.* " //NON-NLS 10560 + "FROM tsk_objects INNER JOIN tsk_files " //NON-NLS 10561 + "ON tsk_objects.obj_id=tsk_files.obj_id " //NON-NLS 10562 + "WHERE (tsk_objects.par_obj_id = ? AND tsk_files.type = ? ) " //NON-NLS 10563 + "ORDER BY tsk_files.dir_type, LOWER(tsk_files.name)"), //NON-NLS 10564 SELECT_FILE_IDS_BY_PARENT("SELECT tsk_files.obj_id AS obj_id " //NON-NLS 10565 + "FROM tsk_objects INNER JOIN tsk_files " //NON-NLS 10566 + "ON tsk_objects.obj_id=tsk_files.obj_id " //NON-NLS 10567 + "WHERE (tsk_objects.par_obj_id = ?)"), //NON-NLS 10568 SELECT_FILE_IDS_BY_PARENT_AND_TYPE("SELECT tsk_files.obj_id AS obj_id " //NON-NLS 10569 + "FROM tsk_objects INNER JOIN tsk_files " //NON-NLS 10570 + "ON tsk_objects.obj_id=tsk_files.obj_id " //NON-NLS 10571 + "WHERE (tsk_objects.par_obj_id = ? " //NON-NLS 10572 + "AND tsk_files.type = ? )"), //NON-NLS 10573 SELECT_FILE_BY_ID("SELECT * FROM tsk_files WHERE obj_id = ? LIMIT 1"), //NON-NLS 10574 SELECT_ARTIFACT_BY_ARTIFACT_OBJ_ID("SELECT * FROM blackboard_artifacts WHERE artifact_obj_id = ? LIMIT 1"), 10575 SELECT_ARTIFACT_BY_ARTIFACT_ID("SELECT * FROM blackboard_artifacts WHERE artifact_id = ? LIMIT 1"), 10576 INSERT_ARTIFACT("INSERT INTO blackboard_artifacts (artifact_id, obj_id, artifact_obj_id, data_source_obj_id, artifact_type_id, review_status_id) " //NON-NLS 10577 + "VALUES (?, ?, ?, ?, ?," + BlackboardArtifact.ReviewStatus.UNDECIDED.getID() + ")"), //NON-NLS 10578 POSTGRESQL_INSERT_ARTIFACT("INSERT INTO blackboard_artifacts (artifact_id, obj_id, artifact_obj_id, data_source_obj_id, artifact_type_id, review_status_id) " //NON-NLS 10579 + "VALUES (DEFAULT, ?, ?, ?, ?," + BlackboardArtifact.ReviewStatus.UNDECIDED.getID() + ")"), //NON-NLS 10580 INSERT_STRING_ATTRIBUTE("INSERT INTO blackboard_attributes (artifact_id, artifact_type_id, source, context, attribute_type_id, value_type, value_text) " //NON-NLS 10581 + "VALUES (?,?,?,?,?,?,?)"), //NON-NLS 10582 INSERT_BYTE_ATTRIBUTE("INSERT INTO blackboard_attributes (artifact_id, artifact_type_id, source, context, attribute_type_id, value_type, value_byte) " //NON-NLS 10583 + "VALUES (?,?,?,?,?,?,?)"), //NON-NLS 10584 INSERT_INT_ATTRIBUTE("INSERT INTO blackboard_attributes (artifact_id, artifact_type_id, source, context, attribute_type_id, value_type, value_int32) " //NON-NLS 10585 + "VALUES (?,?,?,?,?,?,?)"), //NON-NLS 10586 INSERT_LONG_ATTRIBUTE("INSERT INTO blackboard_attributes (artifact_id, artifact_type_id, source, context, attribute_type_id, value_type, value_int64) " //NON-NLS 10587 + "VALUES (?,?,?,?,?,?,?)"), //NON-NLS 10588 INSERT_DOUBLE_ATTRIBUTE("INSERT INTO blackboard_attributes (artifact_id, artifact_type_id, source, context, attribute_type_id, value_type, value_double) " //NON-NLS 10589 + "VALUES (?,?,?,?,?,?,?)"), //NON-NLS 10590 SELECT_FILES_BY_DATA_SOURCE_AND_NAME("SELECT * FROM tsk_files WHERE LOWER(name) LIKE LOWER(?) AND LOWER(name) NOT LIKE LOWER('%journal%') AND data_source_obj_id = ?"), //NON-NLS 10591 SELECT_FILES_BY_DATA_SOURCE_AND_PARENT_PATH_AND_NAME("SELECT * FROM tsk_files WHERE LOWER(name) LIKE LOWER(?) AND LOWER(name) NOT LIKE LOWER('%journal%') AND LOWER(parent_path) LIKE LOWER(?) AND data_source_obj_id = ?"), //NON-NLS 10592 UPDATE_FILE_MD5("UPDATE tsk_files SET md5 = ? WHERE obj_id = ?"), //NON-NLS 10593 UPDATE_IMAGE_MD5("UPDATE tsk_image_info SET md5 = ? WHERE obj_id = ?"), //NON-NLS 10594 UPDATE_IMAGE_SHA1("UPDATE tsk_image_info SET sha1 = ? WHERE obj_id = ?"), //NON-NLS 10595 UPDATE_IMAGE_SHA256("UPDATE tsk_image_info SET sha256 = ? WHERE obj_id = ?"), //NON-NLS 10596 SELECT_IMAGE_MD5("SELECT md5 FROM tsk_image_info WHERE obj_id = ?"), //NON-NLS 10597 SELECT_IMAGE_SHA1("SELECT sha1 FROM tsk_image_info WHERE obj_id = ?"), //NON-NLS 10598 SELECT_IMAGE_SHA256("SELECT sha256 FROM tsk_image_info WHERE obj_id = ?"), //NON-NLS 10599 UPDATE_ACQUISITION_DETAILS("UPDATE data_source_info SET acquisition_details = ? WHERE obj_id = ?"), //NON-NLS 10600 SELECT_ACQUISITION_DETAILS("SELECT acquisition_details FROM data_source_info WHERE obj_id = ?"), //NON-NLS 10601 SELECT_LOCAL_PATH_FOR_FILE("SELECT path FROM tsk_files_path WHERE obj_id = ?"), //NON-NLS 10602 SELECT_ENCODING_FOR_FILE("SELECT encoding_type FROM tsk_files_path WHERE obj_id = ?"), // NON-NLS 10603 SELECT_LOCAL_PATH_AND_ENCODING_FOR_FILE("SELECT path, encoding_type FROM tsk_files_path WHERE obj_id = ?"), // NON_NLS 10604 SELECT_PATH_FOR_FILE("SELECT parent_path FROM tsk_files WHERE obj_id = ?"), //NON-NLS 10605 SELECT_FILE_NAME("SELECT name FROM tsk_files WHERE obj_id = ?"), //NON-NLS 10606 SELECT_DERIVED_FILE("SELECT derived_id, rederive FROM tsk_files_derived WHERE obj_id = ?"), //NON-NLS 10607 SELECT_FILE_DERIVATION_METHOD("SELECT tool_name, tool_version, other FROM tsk_files_derived_method WHERE derived_id = ?"), //NON-NLS 10608 SELECT_MAX_OBJECT_ID("SELECT MAX(obj_id) AS max_obj_id FROM tsk_objects"), //NON-NLS 10609 INSERT_OBJECT("INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?)"), //NON-NLS 10610 INSERT_FILE("INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type, dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, known, mime_type, parent_path, data_source_obj_id,extension) " //NON-NLS 10611 + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), //NON-NLS 10612 INSERT_FILE_SYSTEM_FILE("INSERT INTO tsk_files(obj_id, fs_obj_id, data_source_obj_id, attr_type, attr_id, name, meta_addr, meta_seq, type, has_path, dir_type, meta_type, dir_flags, meta_flags, size, ctime, crtime, atime, mtime, parent_path, extension)" 10613 + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), // NON-NLS 10614 UPDATE_DERIVED_FILE("UPDATE tsk_files SET type = ?, dir_type = ?, meta_type = ?, dir_flags = ?, meta_flags = ?, size= ?, ctime= ?, crtime= ?, atime= ?, mtime= ?, mime_type = ? " 10615 + "WHERE obj_id = ?"), //NON-NLS 10616 INSERT_LAYOUT_FILE("INSERT INTO tsk_file_layout (obj_id, byte_start, byte_len, sequence) " //NON-NLS 10617 + "VALUES (?, ?, ?, ?)"), //NON-NLS 10618 INSERT_LOCAL_PATH("INSERT INTO tsk_files_path (obj_id, path, encoding_type) VALUES (?, ?, ?)"), //NON-NLS 10619 UPDATE_LOCAL_PATH("UPDATE tsk_files_path SET path = ?, encoding_type = ? WHERE obj_id = ?"), //NON-NLS 10620 COUNT_CHILD_OBJECTS_BY_PARENT("SELECT COUNT(obj_id) AS count FROM tsk_objects WHERE par_obj_id = ?"), //NON-NLS 10621 SELECT_FILE_SYSTEM_BY_OBJECT("SELECT fs_obj_id from tsk_files WHERE obj_id=?"), //NON-NLS 10622 SELECT_TAG_NAMES("SELECT * FROM tag_names"), //NON-NLS 10623 SELECT_TAG_NAMES_IN_USE("SELECT * FROM tag_names " //NON-NLS 10624 + "WHERE tag_name_id IN " //NON-NLS 10625 + "(SELECT tag_name_id from content_tags UNION SELECT tag_name_id FROM blackboard_artifact_tags)"), //NON-NLS 10626 SELECT_TAG_NAMES_IN_USE_BY_DATASOURCE("SELECT * FROM tag_names " 10627 + "WHERE tag_name_id IN " 10628 + "( SELECT content_tags.tag_name_id as tag_name_id " 10629 + "FROM content_tags as content_tags, tsk_files as tsk_files" 10630 + " WHERE content_tags.obj_id = tsk_files.obj_id" 10631 + " AND tsk_files.data_source_obj_id = ?" 10632 + " UNION " 10633 + "SELECT artifact_tags.tag_name_id as tag_name_id " 10634 + " FROM blackboard_artifact_tags as artifact_tags, blackboard_artifacts AS arts " 10635 + " WHERE artifact_tags.artifact_id = arts.artifact_id" 10636 + " AND arts.data_source_obj_id = ?" 10637 + " )"), 10638 INSERT_TAG_NAME("INSERT INTO tag_names (display_name, description, color, knownStatus) VALUES (?, ?, ?, ?)"), //NON-NLS 10639 INSERT_CONTENT_TAG("INSERT INTO content_tags (obj_id, tag_name_id, comment, begin_byte_offset, end_byte_offset, examiner_id) VALUES (?, ?, ?, ?, ?, ?)"), //NON-NLS 10640 DELETE_CONTENT_TAG("DELETE FROM content_tags WHERE tag_id = ?"), //NON-NLS 10641 COUNT_CONTENT_TAGS_BY_TAG_NAME("SELECT COUNT(*) AS count FROM content_tags WHERE tag_name_id = ?"), //NON-NLS 10642 COUNT_CONTENT_TAGS_BY_TAG_NAME_BY_DATASOURCE( 10643 "SELECT COUNT(*) AS count FROM content_tags as content_tags, tsk_files as tsk_files WHERE content_tags.obj_id = tsk_files.obj_id" 10644 + " AND content_tags.tag_name_id = ? " 10645 + " AND tsk_files.data_source_obj_id = ? " 10646 ), 10647 SELECT_CONTENT_TAGS("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name " 10648 + "FROM content_tags " 10649 + "INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id " 10650 + "LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id"), //NON-NLS 10651 SELECT_CONTENT_TAGS_BY_TAG_NAME("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tsk_examiners.login_name " 10652 + "FROM content_tags " 10653 + "LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id " 10654 + "WHERE tag_name_id = ?"), //NON-NLS 10655 SELECT_CONTENT_TAGS_BY_TAG_NAME_BY_DATASOURCE("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name " 10656 + "FROM content_tags as content_tags, tsk_files as tsk_files, tag_names as tag_names " 10657 + "LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id " 10658 + "WHERE content_tags.obj_id = tsk_files.obj_id" 10659 + " AND content_tags.tag_name_id = tag_names.tag_name_id" 10660 + " AND content_tags.tag_name_id = ?" 10661 + " AND tsk_files.data_source_obj_id = ? "), 10662 SELECT_CONTENT_TAG_BY_ID("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name " 10663 + "FROM content_tags " 10664 + "INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id " 10665 + "LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id " 10666 + "WHERE tag_id = ?"), //NON-NLS 10667 SELECT_CONTENT_TAGS_BY_CONTENT("SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name " 10668 + "FROM content_tags " 10669 + "INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id " 10670 + "LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id " 10671 + "WHERE content_tags.obj_id = ?"), //NON-NLS 10672 INSERT_ARTIFACT_TAG("INSERT INTO blackboard_artifact_tags (artifact_id, tag_name_id, comment, examiner_id) " 10673 + "VALUES (?, ?, ?, ?)"), //NON-NLS 10674 DELETE_ARTIFACT_TAG("DELETE FROM blackboard_artifact_tags WHERE tag_id = ?"), //NON-NLS 10675 SELECT_ARTIFACT_TAGS("SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name " 10676 + "FROM blackboard_artifact_tags " 10677 + "INNER JOIN tag_names ON blackboard_artifact_tags.tag_name_id = tag_names.tag_name_id " 10678 + "LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id"), //NON-NLS 10679 COUNT_ARTIFACTS_BY_TAG_NAME("SELECT COUNT(*) AS count FROM blackboard_artifact_tags WHERE tag_name_id = ?"), //NON-NLS 10680 COUNT_ARTIFACTS_BY_TAG_NAME_BY_DATASOURCE("SELECT COUNT(*) AS count FROM blackboard_artifact_tags as artifact_tags, blackboard_artifacts AS arts WHERE artifact_tags.artifact_id = arts.artifact_id" 10681 + " AND artifact_tags.tag_name_id = ?" 10682 + " AND arts.data_source_obj_id = ? "), 10683 SELECT_ARTIFACT_TAGS_BY_TAG_NAME("SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tsk_examiners.login_name " 10684 + "FROM blackboard_artifact_tags " 10685 + "LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id " 10686 + "WHERE tag_name_id = ?"), //NON-NLS 10687 SELECT_ARTIFACT_TAGS_BY_TAG_NAME_BY_DATASOURCE("SELECT artifact_tags.tag_id, artifact_tags.artifact_id, artifact_tags.tag_name_id, artifact_tags.comment, arts.obj_id, arts.artifact_obj_id, arts.data_source_obj_id, arts.artifact_type_id, arts.review_status_id, tsk_examiners.login_name " 10688 + "FROM blackboard_artifact_tags as artifact_tags, blackboard_artifacts AS arts " 10689 + "LEFT OUTER JOIN tsk_examiners ON artifact_tags.examiner_id = tsk_examiners.examiner_id " 10690 + "WHERE artifact_tags.artifact_id = arts.artifact_id" 10691 + " AND artifact_tags.tag_name_id = ? " 10692 + " AND arts.data_source_obj_id = ? "), 10693 SELECT_ARTIFACT_TAG_BY_ID("SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name " 10694 + "FROM blackboard_artifact_tags " 10695 + "INNER JOIN tag_names ON blackboard_artifact_tags.tag_name_id = tag_names.tag_name_id " 10696 + "LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id " 10697 + "WHERE blackboard_artifact_tags.tag_id = ?"), //NON-NLS 10698 SELECT_ARTIFACT_TAGS_BY_ARTIFACT("SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name " 10699 + "FROM blackboard_artifact_tags " 10700 + "INNER JOIN tag_names ON blackboard_artifact_tags.tag_name_id = tag_names.tag_name_id " 10701 + "LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id " 10702 + "WHERE blackboard_artifact_tags.artifact_id = ?"), //NON-NLS 10703 SELECT_REPORTS("SELECT * FROM reports"), //NON-NLS 10704 SELECT_REPORT_BY_ID("SELECT * FROM reports WHERE obj_id = ?"), //NON-NLS 10705 INSERT_REPORT("INSERT INTO reports (obj_id, path, crtime, src_module_name, report_name) VALUES (?, ?, ?, ?, ?)"), //NON-NLS 10706 DELETE_REPORT("DELETE FROM reports WHERE reports.obj_id = ?"), //NON-NLS 10707 INSERT_INGEST_JOB("INSERT INTO ingest_jobs (obj_id, host_name, start_date_time, end_date_time, status_id, settings_dir) VALUES (?, ?, ?, ?, ?, ?)"), //NON-NLS 10708 INSERT_INGEST_MODULE("INSERT INTO ingest_modules (display_name, unique_name, type_id, version) VALUES(?, ?, ?, ?)"), //NON-NLS 10709 SELECT_ATTR_BY_VALUE_BYTE("SELECT source FROM blackboard_attributes WHERE artifact_id = ? AND attribute_type_id = ? AND value_type = 4 AND value_byte = ?"), //NON-NLS 10710 UPDATE_ATTR_BY_VALUE_BYTE("UPDATE blackboard_attributes SET source = ? WHERE artifact_id = ? AND attribute_type_id = ? AND value_type = 4 AND value_byte = ?"), //NON-NLS 10711 UPDATE_IMAGE_PATH("UPDATE tsk_image_names SET name = ? WHERE obj_id = ?"), // NON-NLS 10712 SELECT_ARTIFACT_OBJECTIDS_BY_PARENT("SELECT blackboard_artifacts.artifact_obj_id AS artifact_obj_id " //NON-NLS 10713 + "FROM tsk_objects INNER JOIN blackboard_artifacts " //NON-NLS 10714 + "ON tsk_objects.obj_id=blackboard_artifacts.obj_id " //NON-NLS 10715 + "WHERE (tsk_objects.par_obj_id = ?)"), 10716 INSERT_OR_UPDATE_TAG_NAME_POSTGRES("INSERT INTO tag_names (display_name, description, color, knownStatus) VALUES (?, ?, ?, ?) ON CONFLICT (display_name) DO UPDATE SET description = ?, color = ?, knownStatus = ?"), 10717 INSERT_OR_UPDATE_TAG_NAME_SQLITE("WITH new (display_name, description, color, knownStatus) " 10718 + "AS ( VALUES(?, ?, ?, ?)) INSERT OR REPLACE INTO tag_names " 10719 + "(tag_name_id, display_name, description, color, knownStatus) " 10720 + "SELECT old.tag_name_id, new.display_name, new.description, new.color, new.knownStatus " 10721 + "FROM new LEFT JOIN tag_names AS old ON new.display_name = old.display_name"), 10722 SELECT_EXAMINER_BY_ID("SELECT * FROM tsk_examiners WHERE examiner_id = ?"), 10723 SELECT_EXAMINER_BY_LOGIN_NAME("SELECT * FROM tsk_examiners WHERE login_name = ?"), 10724 UPDATE_FILE_NAME("UPDATE tsk_files SET name = ? WHERE obj_id = ?"), 10725 UPDATE_IMAGE_NAME("UPDATE tsk_image_info SET display_name = ? WHERE obj_id = ?"), 10726 DELETE_IMAGE_NAME("DELETE FROM tsk_image_names WHERE obj_id = ?"), 10727 INSERT_IMAGE_NAME("INSERT INTO tsk_image_names (obj_id, name, sequence) VALUES (?, ?, ?)"), 10728 INSERT_IMAGE_INFO("INSERT INTO tsk_image_info (obj_id, type, ssize, tzone, size, md5, sha1, sha256, display_name)" 10729 + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"), 10730 INSERT_DATA_SOURCE_INFO("INSERT INTO data_source_info (obj_id, device_id, time_zone) VALUES (?, ?, ?)"), 10731 INSERT_VS_INFO("INSERT INTO tsk_vs_info (obj_id, vs_type, img_offset, block_size) VALUES (?, ?, ?, ?)"), 10732 INSERT_VS_PART_SQLITE("INSERT INTO tsk_vs_parts (obj_id, addr, start, length, desc, flags) VALUES (?, ?, ?, ?, ?, ?)"), 10733 INSERT_VS_PART_POSTGRESQL("INSERT INTO tsk_vs_parts (obj_id, addr, start, length, descr, flags) VALUES (?, ?, ?, ?, ?, ?)"), 10734 INSERT_FS_INFO("INSERT INTO tsk_fs_info (obj_id, img_offset, fs_type, block_size, block_count, root_inum, first_inum, last_inum, display_name)" 10735 + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"); 10736 10737 private final String sql; 10738 10739 private PREPARED_STATEMENT(String sql) { 10740 this.sql = sql; 10741 } 10742 10743 String getSQL() { 10744 return sql; 10745 } 10746 } 10747 10748 /** 10749 * A class for the connection pool. This class will hand out connections of 10750 * the appropriate type based on the subclass that is calling 10751 * getPooledConnection(); 10752 */ 10753 abstract private class ConnectionPool { 10754 10755 private PooledDataSource pooledDataSource; 10756 10757 public ConnectionPool() { 10758 pooledDataSource = null; 10759 } 10760 10761 CaseDbConnection getConnection() throws TskCoreException { 10762 if (pooledDataSource == null) { 10763 throw new TskCoreException("Error getting case database connection - case is closed"); 10764 } 10765 try { 10766 return getPooledConnection(); 10767 } catch (SQLException exp) { 10768 throw new TskCoreException(exp.getMessage()); 10769 } 10770 } 10771 10772 void close() throws TskCoreException { 10773 if (pooledDataSource != null) { 10774 try { 10775 pooledDataSource.close(); 10776 } catch (SQLException exp) { 10777 throw new TskCoreException(exp.getMessage()); 10778 } finally { 10779 pooledDataSource = null; 10780 } 10781 } 10782 } 10783 10784 abstract CaseDbConnection getPooledConnection() throws SQLException; 10785 10786 public PooledDataSource getPooledDataSource() { 10787 return pooledDataSource; 10788 } 10789 10790 public void setPooledDataSource(PooledDataSource pooledDataSource) { 10791 this.pooledDataSource = pooledDataSource; 10792 } 10793 } 10794 10795 /** 10796 * Handles the initial setup of SQLite database connections, as well as 10797 * overriding getPooledConnection() 10798 */ 10799 private final class SQLiteConnections extends ConnectionPool { 10800 10801 private final Map<String, String> configurationOverrides = new HashMap<String, String>(); 10802 10803 SQLiteConnections(String dbPath) throws SQLException { 10804 configurationOverrides.put("acquireIncrement", "2"); 10805 configurationOverrides.put("initialPoolSize", "5"); 10806 configurationOverrides.put("minPoolSize", "5"); 10807 /* 10808 * NOTE: max pool size and max statements are related. If you 10809 * increase max pool size, then also increase statements. 10810 */ 10811 configurationOverrides.put("maxPoolSize", "20"); 10812 configurationOverrides.put("maxStatements", "200"); 10813 configurationOverrides.put("maxStatementsPerConnection", "20"); 10814 10815 SQLiteConfig config = new SQLiteConfig(); 10816 config.setSynchronous(SQLiteConfig.SynchronousMode.OFF); // Reduce I/O operations, we have no OS crash recovery anyway. 10817 config.setReadUncommited(true); 10818 config.enforceForeignKeys(true); // Enforce foreign key constraints. 10819 SQLiteDataSource unpooled = new SQLiteDataSource(config); 10820 unpooled.setUrl("jdbc:sqlite:" + dbPath); 10821 setPooledDataSource((PooledDataSource) DataSources.pooledDataSource(unpooled, configurationOverrides)); 10822 } 10823 10824 @Override 10825 public CaseDbConnection getPooledConnection() throws SQLException { 10826 return new SQLiteConnection(getPooledDataSource().getConnection()); 10827 } 10828 } 10829 10830 /** 10831 * Handles the initial setup of PostgreSQL database connections, as well as 10832 * overriding getPooledConnection() 10833 */ 10834 private final class PostgreSQLConnections extends ConnectionPool { 10835 10836 PostgreSQLConnections(String host, int port, String dbName, String userName, String password) throws PropertyVetoException, UnsupportedEncodingException { 10837 ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource(); 10838 comboPooledDataSource.setDriverClass("org.postgresql.Driver"); //loads the jdbc driver 10839 comboPooledDataSource.setJdbcUrl("jdbc:postgresql://" + host + ":" + port + "/" 10840 + URLEncoder.encode(dbName, StandardCharsets.UTF_8.toString())); 10841 comboPooledDataSource.setUser(userName); 10842 comboPooledDataSource.setPassword(password); 10843 comboPooledDataSource.setAcquireIncrement(2); 10844 comboPooledDataSource.setInitialPoolSize(5); 10845 comboPooledDataSource.setMinPoolSize(5); 10846 /* 10847 * NOTE: max pool size and max statements are related. If you 10848 * increase max pool size, then also increase statements. 10849 */ 10850 comboPooledDataSource.setMaxPoolSize(20); 10851 comboPooledDataSource.setMaxStatements(200); 10852 comboPooledDataSource.setMaxStatementsPerConnection(20); 10853 setPooledDataSource(comboPooledDataSource); 10854 } 10855 10856 @Override 10857 public CaseDbConnection getPooledConnection() throws SQLException { 10858 return new PostgreSQLConnection(getPooledDataSource().getConnection()); 10859 } 10860 } 10861 10862 /** 10863 * An abstract base class for case database connection objects. 10864 */ 10865 abstract class CaseDbConnection implements AutoCloseable { 10866 10867 static final int SLEEP_LENGTH_IN_MILLISECONDS = 5000; 10868 static final int MAX_RETRIES = 20; //MAX_RETRIES * SLEEP_LENGTH_IN_MILLESECONDS = max time to hang attempting connection 10869 10870 private class CreateStatement implements DbCommand { 10871 10872 private final Connection connection; 10873 private Statement statement = null; 10874 10875 CreateStatement(Connection connection) { 10876 this.connection = connection; 10877 } 10878 10879 Statement getStatement() { 10880 return statement; 10881 } 10882 10883 @Override 10884 public void execute() throws SQLException { 10885 statement = connection.createStatement(); 10886 } 10887 } 10888 10889 private class SetAutoCommit implements DbCommand { 10890 10891 private final Connection connection; 10892 private final boolean mode; 10893 10894 SetAutoCommit(Connection connection, boolean mode) { 10895 this.connection = connection; 10896 this.mode = mode; 10897 } 10898 10899 @Override 10900 public void execute() throws SQLException { 10901 connection.setAutoCommit(mode); 10902 } 10903 } 10904 10905 private class Commit implements DbCommand { 10906 10907 private final Connection connection; 10908 10909 Commit(Connection connection) { 10910 this.connection = connection; 10911 } 10912 10913 @Override 10914 public void execute() throws SQLException { 10915 connection.commit(); 10916 } 10917 } 10918 10919 private class ExecuteQuery implements DbCommand { 10920 10921 private final Statement statement; 10922 private final String query; 10923 private ResultSet resultSet; 10924 10925 ExecuteQuery(Statement statement, String query) { 10926 this.statement = statement; 10927 this.query = query; 10928 } 10929 10930 ResultSet getResultSet() { 10931 return resultSet; 10932 } 10933 10934 @Override 10935 public void execute() throws SQLException { 10936 resultSet = statement.executeQuery(query); 10937 } 10938 } 10939 10940 private class ExecutePreparedStatementQuery implements DbCommand { 10941 10942 private final PreparedStatement preparedStatement; 10943 private ResultSet resultSet; 10944 10945 ExecutePreparedStatementQuery(PreparedStatement preparedStatement) { 10946 this.preparedStatement = preparedStatement; 10947 } 10948 10949 ResultSet getResultSet() { 10950 return resultSet; 10951 } 10952 10953 @Override 10954 public void execute() throws SQLException { 10955 resultSet = preparedStatement.executeQuery(); 10956 } 10957 } 10958 10959 private class ExecutePreparedStatementUpdate implements DbCommand { 10960 10961 private final PreparedStatement preparedStatement; 10962 10963 ExecutePreparedStatementUpdate(PreparedStatement preparedStatement) { 10964 this.preparedStatement = preparedStatement; 10965 } 10966 10967 @Override 10968 public void execute() throws SQLException { 10969 preparedStatement.executeUpdate(); 10970 } 10971 } 10972 10973 private class ExecuteStatementUpdate implements DbCommand { 10974 10975 private final Statement statement; 10976 private final String updateCommand; 10977 10978 ExecuteStatementUpdate(Statement statement, String updateCommand) { 10979 this.statement = statement; 10980 this.updateCommand = updateCommand; 10981 } 10982 10983 @Override 10984 public void execute() throws SQLException { 10985 statement.executeUpdate(updateCommand); 10986 } 10987 } 10988 10989 private class ExecuteStatementUpdateGenerateKeys implements DbCommand { 10990 10991 private final Statement statement; 10992 private final int generateKeys; 10993 private final String updateCommand; 10994 10995 ExecuteStatementUpdateGenerateKeys(Statement statement, String updateCommand, int generateKeys) { 10996 this.statement = statement; 10997 this.generateKeys = generateKeys; 10998 this.updateCommand = updateCommand; 10999 } 11000 11001 @Override 11002 public void execute() throws SQLException { 11003 statement.executeUpdate(updateCommand, generateKeys); 11004 } 11005 } 11006 11007 private class PrepareStatement implements DbCommand { 11008 11009 private final Connection connection; 11010 private final String input; 11011 private PreparedStatement preparedStatement = null; 11012 11013 PrepareStatement(Connection connection, String input) { 11014 this.connection = connection; 11015 this.input = input; 11016 } 11017 11018 PreparedStatement getPreparedStatement() { 11019 return preparedStatement; 11020 } 11021 11022 @Override 11023 public void execute() throws SQLException { 11024 preparedStatement = connection.prepareStatement(input); 11025 } 11026 } 11027 11028 private class PrepareStatementGenerateKeys implements DbCommand { 11029 11030 private final Connection connection; 11031 private final String input; 11032 private final int generateKeys; 11033 private PreparedStatement preparedStatement = null; 11034 11035 PrepareStatementGenerateKeys(Connection connection, String input, int generateKeysInput) { 11036 this.connection = connection; 11037 this.input = input; 11038 this.generateKeys = generateKeysInput; 11039 } 11040 11041 PreparedStatement getPreparedStatement() { 11042 return preparedStatement; 11043 } 11044 11045 @Override 11046 public void execute() throws SQLException { 11047 preparedStatement = connection.prepareStatement(input, generateKeys); 11048 } 11049 } 11050 11051 abstract void executeCommand(DbCommand command) throws SQLException; 11052 11053 private final Connection connection; 11054 private final Map<PREPARED_STATEMENT, PreparedStatement> preparedStatements; 11055 11056 CaseDbConnection(Connection connection) { 11057 this.connection = connection; 11058 preparedStatements = new EnumMap<PREPARED_STATEMENT, PreparedStatement>(PREPARED_STATEMENT.class); 11059 } 11060 11061 boolean isOpen() { 11062 return this.connection != null; 11063 } 11064 11065 PreparedStatement getPreparedStatement(PREPARED_STATEMENT statementKey) throws SQLException { 11066 return getPreparedStatement(statementKey, Statement.NO_GENERATED_KEYS); 11067 } 11068 11069 PreparedStatement getPreparedStatement(PREPARED_STATEMENT statementKey, int generateKeys) throws SQLException { 11070 // Lazy statement preparation. 11071 PreparedStatement statement; 11072 if (this.preparedStatements.containsKey(statementKey)) { 11073 statement = this.preparedStatements.get(statementKey); 11074 } else { 11075 statement = prepareStatement(statementKey.getSQL(), generateKeys); 11076 this.preparedStatements.put(statementKey, statement); 11077 } 11078 return statement; 11079 } 11080 11081 PreparedStatement prepareStatement(String sqlStatement, int generateKeys) throws SQLException { 11082 PrepareStatement prepareStatement = new PrepareStatement(this.getConnection(), sqlStatement); 11083 executeCommand(prepareStatement); 11084 return prepareStatement.getPreparedStatement(); 11085 } 11086 11087 Statement createStatement() throws SQLException { 11088 CreateStatement createStatement = new CreateStatement(this.connection); 11089 executeCommand(createStatement); 11090 return createStatement.getStatement(); 11091 } 11092 11093 void beginTransaction() throws SQLException { 11094 SetAutoCommit setAutoCommit = new SetAutoCommit(connection, false); 11095 executeCommand(setAutoCommit); 11096 } 11097 11098 void commitTransaction() throws SQLException { 11099 Commit commit = new Commit(connection); 11100 executeCommand(commit); 11101 // You must turn auto commit back on when done with the transaction. 11102 SetAutoCommit setAutoCommit = new SetAutoCommit(connection, true); 11103 executeCommand(setAutoCommit); 11104 } 11105 11106 /** 11107 * A rollback that logs exceptions and does not throw, intended for 11108 * "internal" use in SleuthkitCase methods where the exception that 11109 * motivated the rollback is the exception to report to the client. 11110 */ 11111 void rollbackTransaction() { 11112 try { 11113 connection.rollback(); 11114 } catch (SQLException e) { 11115 logger.log(Level.SEVERE, "Error rolling back transaction", e); 11116 } 11117 try { 11118 connection.setAutoCommit(true); 11119 } catch (SQLException e) { 11120 logger.log(Level.SEVERE, "Error restoring auto-commit", e); 11121 } 11122 } 11123 11124 /** 11125 * A rollback that throws, intended for use by the CaseDbTransaction 11126 * class where client code is managing the transaction and the client 11127 * may wish to know that the rollback failed. 11128 * 11129 * @throws SQLException 11130 */ 11131 void rollbackTransactionWithThrow() throws SQLException { 11132 try { 11133 connection.rollback(); 11134 } finally { 11135 connection.setAutoCommit(true); 11136 } 11137 } 11138 11139 ResultSet executeQuery(Statement statement, String query) throws SQLException { 11140 ExecuteQuery queryCommand = new ExecuteQuery(statement, query); 11141 executeCommand(queryCommand); 11142 return queryCommand.getResultSet(); 11143 } 11144 11145 /** 11146 * 11147 * @param statement The SQL statement to execute 11148 * 11149 * @return returns the ResultSet from the execution of the query 11150 * 11151 * @throws SQLException \ref query_database_page \ref 11152 * insert_and_update_database_page 11153 */ 11154 ResultSet executeQuery(PreparedStatement statement) throws SQLException { 11155 ExecutePreparedStatementQuery executePreparedStatementQuery = new ExecutePreparedStatementQuery(statement); 11156 executeCommand(executePreparedStatementQuery); 11157 return executePreparedStatementQuery.getResultSet(); 11158 } 11159 11160 void executeUpdate(Statement statement, String update) throws SQLException { 11161 executeUpdate(statement, update, Statement.NO_GENERATED_KEYS); 11162 } 11163 11164 void executeUpdate(Statement statement, String update, int generateKeys) throws SQLException { 11165 ExecuteStatementUpdate executeStatementUpdate = new ExecuteStatementUpdate(statement, update); 11166 executeCommand(executeStatementUpdate); 11167 } 11168 11169 void executeUpdate(PreparedStatement statement) throws SQLException { 11170 ExecutePreparedStatementUpdate executePreparedStatementUpdate = new ExecutePreparedStatementUpdate(statement); 11171 executeCommand(executePreparedStatementUpdate); 11172 } 11173 11174 /** 11175 * Close the connection to the database. 11176 */ 11177 @Override 11178 public void close() { 11179 try { 11180 connection.close(); 11181 } catch (SQLException ex) { 11182 logger.log(Level.SEVERE, "Unable to close connection to case database", ex); 11183 } 11184 } 11185 11186 Connection getConnection() { 11187 return this.connection; 11188 } 11189 } 11190 11191 /** 11192 * A connection to an SQLite case database. 11193 */ 11194 private final class SQLiteConnection extends CaseDbConnection { 11195 11196 private static final int DATABASE_LOCKED_ERROR = 0; // This should be 6 according to documentation, but it has been observed to be 0. 11197 private static final int SQLITE_BUSY_ERROR = 5; 11198 11199 SQLiteConnection(Connection conn) { 11200 super(conn); 11201 } 11202 11203 @Override 11204 void executeCommand(DbCommand command) throws SQLException { 11205 int retryCounter = 0; 11206 while (true) { 11207 try { 11208 command.execute(); // Perform the operation 11209 break; 11210 } catch (SQLException ex) { 11211 if ((ex.getErrorCode() == SQLITE_BUSY_ERROR || ex.getErrorCode() == DATABASE_LOCKED_ERROR) && retryCounter < MAX_RETRIES) { 11212 try { 11213 11214 // We do not notify of error here, as this is not an 11215 // error condition. It is likely a temporary busy or 11216 // locked issue and we will retry. 11217 retryCounter++; 11218 Thread.sleep(SLEEP_LENGTH_IN_MILLISECONDS); 11219 } catch (InterruptedException exp) { 11220 Logger.getLogger(SleuthkitCase.class.getName()).log(Level.WARNING, "Unexpectedly unable to wait for database.", exp); 11221 } 11222 } else { 11223 throw ex; 11224 } 11225 } 11226 } 11227 } 11228 } 11229 11230 /** 11231 * A connection to a PostgreSQL case database. 11232 */ 11233 private final class PostgreSQLConnection extends CaseDbConnection { 11234 11235 private final String COMMUNICATION_ERROR = PSQLState.COMMUNICATION_ERROR.getState(); 11236 private final String SYSTEM_ERROR = PSQLState.SYSTEM_ERROR.getState(); 11237 private final String UNKNOWN_STATE = PSQLState.UNKNOWN_STATE.getState(); 11238 private static final int MAX_RETRIES = 3; 11239 11240 PostgreSQLConnection(Connection conn) { 11241 super(conn); 11242 } 11243 11244 @Override 11245 void executeUpdate(Statement statement, String update, int generateKeys) throws SQLException { 11246 CaseDbConnection.ExecuteStatementUpdateGenerateKeys executeStatementUpdateGenerateKeys = new CaseDbConnection.ExecuteStatementUpdateGenerateKeys(statement, update, generateKeys); 11247 executeCommand(executeStatementUpdateGenerateKeys); 11248 } 11249 11250 @Override 11251 PreparedStatement prepareStatement(String sqlStatement, int generateKeys) throws SQLException { 11252 CaseDbConnection.PrepareStatementGenerateKeys prepareStatementGenerateKeys = new CaseDbConnection.PrepareStatementGenerateKeys(this.getConnection(), sqlStatement, generateKeys); 11253 executeCommand(prepareStatementGenerateKeys); 11254 return prepareStatementGenerateKeys.getPreparedStatement(); 11255 } 11256 11257 @Override 11258 void executeCommand(DbCommand command) throws SQLException { 11259 SQLException lastException = null; 11260 for (int retries = 0; retries < MAX_RETRIES; retries++) { 11261 try { 11262 command.execute(); 11263 lastException = null; // reset since we had a successful execution 11264 break; 11265 } catch (SQLException ex) { 11266 lastException = ex; 11267 String sqlState = ex.getSQLState(); 11268 if (sqlState == null || sqlState.equals(COMMUNICATION_ERROR) || sqlState.equals(SYSTEM_ERROR) || sqlState.equals(UNKNOWN_STATE)) { 11269 try { 11270 Thread.sleep(SLEEP_LENGTH_IN_MILLISECONDS); 11271 } catch (InterruptedException exp) { 11272 Logger.getLogger(SleuthkitCase.class.getName()).log(Level.WARNING, "Unexpectedly unable to wait for database.", exp); 11273 } 11274 } else { 11275 throw ex; 11276 } 11277 } 11278 } 11279 11280 // rethrow the exception if we bailed because of too many retries 11281 if (lastException != null) { 11282 throw lastException; 11283 } 11284 } 11285 } 11286 11287 /** 11288 * Wraps the transactional capabilities of a CaseDbConnection object to 11289 * support use cases where control of a transaction is given to a 11290 * SleuthkitCase client. Note that this class does not implement the 11291 * Transaction interface because that sort of flexibility and its associated 11292 * complexity is not needed. Also, TskCoreExceptions are thrown to be 11293 * consistent with the outer SleuthkitCase class. 11294 */ 11295 public static final class CaseDbTransaction { 11296 11297 private final CaseDbConnection connection; 11298 private boolean hasWriteLock = false; 11299 private SleuthkitCase sleuthkitCase; 11300 11301 private CaseDbTransaction(SleuthkitCase sleuthkitCase, CaseDbConnection connection) throws TskCoreException { 11302 this.connection = connection; 11303 this.sleuthkitCase = sleuthkitCase; 11304 try { 11305 this.connection.beginTransaction(); 11306 } catch (SQLException ex) { 11307 throw new TskCoreException("Failed to create transaction on case database", ex); 11308 } 11309 } 11310 11311 /** 11312 * The implementations of the public APIs that take a CaseDbTransaction 11313 * object need access to the underlying CaseDbConnection. 11314 * 11315 * @return The CaseDbConnection instance for this instance of 11316 * CaseDbTransaction. 11317 */ 11318 CaseDbConnection getConnection() { 11319 return this.connection; 11320 } 11321 11322 /** 11323 * Obtain a write lock for this transaction. Only one will be obtained 11324 * (no matter how many times it is called) and will be released when 11325 * commit or rollback is called. 11326 * 11327 * If this is not used, you risk deadlock because this transaction can 11328 * lock up SQLite and make it "busy" and another thread may get a write 11329 * lock to the DB, but not be able to do anything because the DB is 11330 * busy. 11331 */ 11332 void acquireSingleUserCaseWriteLock() { 11333 if (!hasWriteLock) { 11334 hasWriteLock = true; 11335 sleuthkitCase.acquireSingleUserCaseWriteLock(); 11336 } 11337 } 11338 11339 /** 11340 * Commits the transaction on the case database that was begun when this 11341 * object was constructed. 11342 * 11343 * @throws TskCoreException 11344 */ 11345 public void commit() throws TskCoreException { 11346 try { 11347 this.connection.commitTransaction(); 11348 } catch (SQLException ex) { 11349 throw new TskCoreException("Failed to commit transaction on case database", ex); 11350 } finally { 11351 close(); 11352 } 11353 } 11354 11355 /** 11356 * Rolls back the transaction on the case database that was begun when 11357 * this object was constructed. 11358 * 11359 * @throws TskCoreException 11360 */ 11361 public void rollback() throws TskCoreException { 11362 try { 11363 this.connection.rollbackTransactionWithThrow(); 11364 } catch (SQLException ex) { 11365 throw new TskCoreException("Case database transaction rollback failed", ex); 11366 } finally { 11367 close(); 11368 } 11369 } 11370 11371 /** 11372 * Close the database connection 11373 * 11374 */ 11375 void close() { 11376 this.connection.close(); 11377 if (hasWriteLock) { 11378 sleuthkitCase.releaseSingleUserCaseWriteLock(); 11379 hasWriteLock = false; 11380 } 11381 } 11382 } 11383 11384 /** 11385 * The CaseDbQuery supports the use case where developers have a need for 11386 * data that is not exposed through the SleuthkitCase API. A CaseDbQuery 11387 * instance gets created through the SleuthkitCase executeDbQuery() method. 11388 * It wraps the ResultSet and takes care of acquiring and releasing the 11389 * appropriate database lock. It implements AutoCloseable so that it can be 11390 * used in a try-with -resources block freeing developers from having to 11391 * remember to close the result set and releasing the lock. 11392 */ 11393 public final class CaseDbQuery implements AutoCloseable { 11394 11395 private ResultSet resultSet; 11396 private CaseDbConnection connection; 11397 11398 private CaseDbQuery(String query) throws TskCoreException { 11399 this(query, false); 11400 } 11401 11402 private CaseDbQuery(String query, boolean allowWriteQuery) throws TskCoreException { 11403 if (!allowWriteQuery) { 11404 if (!query.regionMatches(true, 0, "SELECT", 0, "SELECT".length())) { 11405 throw new TskCoreException("Unsupported query: Only SELECT queries are supported."); 11406 } 11407 } 11408 try { 11409 connection = connections.getConnection(); 11410 } catch (TskCoreException ex) { 11411 throw new TskCoreException("Error getting connection for query: ", ex); 11412 } 11413 11414 try { 11415 SleuthkitCase.this.acquireSingleUserCaseReadLock(); 11416 resultSet = connection.executeQuery(connection.createStatement(), query); 11417 } catch (SQLException ex) { 11418 SleuthkitCase.this.releaseSingleUserCaseReadLock(); 11419 throw new TskCoreException("Error executing query: ", ex); 11420 } 11421 } 11422 11423 /** 11424 * Get the result set for this query. 11425 * 11426 * @return The result set. 11427 */ 11428 public ResultSet getResultSet() { 11429 return resultSet; 11430 } 11431 11432 @Override 11433 public void close() throws TskCoreException { 11434 try { 11435 if (resultSet != null) { 11436 final Statement statement = resultSet.getStatement(); 11437 if (statement != null) { 11438 statement.close(); 11439 } 11440 resultSet.close(); 11441 } 11442 connection.close(); 11443 } catch (SQLException ex) { 11444 throw new TskCoreException("Error closing query: ", ex); 11445 } finally { 11446 SleuthkitCase.this.releaseSingleUserCaseReadLock(); 11447 } 11448 } 11449 } 11450 11451 /** 11452 * Add an observer for SleuthkitCase errors. 11453 * 11454 * @param observer The observer to add. 11455 * 11456 * @deprecated Catch exceptions instead. 11457 */ 11458 @Deprecated 11459 public void addErrorObserver(ErrorObserver observer) { 11460 sleuthkitCaseErrorObservers.add(observer); 11461 } 11462 11463 /** 11464 * Remove an observer for SleuthkitCase errors. 11465 * 11466 * @param observer The observer to remove. 11467 * 11468 * @deprecated Catch exceptions instead. 11469 */ 11470 @Deprecated 11471 public void removeErrorObserver(ErrorObserver observer) { 11472 int i = sleuthkitCaseErrorObservers.indexOf(observer); 11473 if (i >= 0) { 11474 sleuthkitCaseErrorObservers.remove(i); 11475 } 11476 } 11477 11478 /** 11479 * Submit an error to all clients that are listening. 11480 * 11481 * @param context The context in which the error occurred. 11482 * @param errorMessage A description of the error that occurred. 11483 * 11484 * @deprecated Catch exceptions instead. 11485 */ 11486 @Deprecated 11487 public void submitError(String context, String errorMessage) { 11488 for (ErrorObserver observer : sleuthkitCaseErrorObservers) { 11489 if (observer != null) { 11490 try { 11491 observer.receiveError(context, errorMessage); 11492 } catch (Exception ex) { 11493 logger.log(Level.SEVERE, "Observer client unable to receive message: {0}, {1}", new Object[]{context, errorMessage, ex}); 11494 11495 } 11496 } 11497 } 11498 } 11499 11500 /** 11501 * Notifies observers of errors in the SleuthkitCase. 11502 * 11503 * @deprecated Catch exceptions instead. 11504 */ 11505 @Deprecated 11506 public interface ErrorObserver { 11507 11508 /** 11509 * List of arguments for the context string parameters. This does not 11510 * preclude the use of arbitrary context strings by client code, but it 11511 * does provide a place to define standard context strings to allow 11512 * filtering of notifications by implementations of ErrorObserver. 11513 */ 11514 public enum Context { 11515 11516 /** 11517 * Error occurred while reading image content. 11518 */ 11519 IMAGE_READ_ERROR("Image File Read Error"), 11520 /** 11521 * Error occurred while reading database content. 11522 */ 11523 DATABASE_READ_ERROR("Database Read Error"); 11524 11525 private final String contextString; 11526 11527 private Context(String context) { 11528 this.contextString = context; 11529 } 11530 11531 public String getContextString() { 11532 return contextString; 11533 } 11534 }; 11535 11536 void receiveError(String context, String errorMessage); 11537 } 11538 11539 /** 11540 * Given an object id, works up the tree of ancestors to the data source for 11541 * the object and gets the object id of the data source. The trivial case 11542 * where the input object id is for a source is handled. 11543 * 11544 * @param objectId An object id. 11545 * 11546 * @return A data source object id. 11547 * 11548 */ 11549 @Deprecated 11550 long getDataSourceObjectId(long objectId) { 11551 try { 11552 CaseDbConnection connection = connections.getConnection(); 11553 try { 11554 return getDataSourceObjectId(connection, objectId); 11555 } finally { 11556 connection.close(); 11557 } 11558 } catch (TskCoreException ex) { 11559 logger.log(Level.SEVERE, "Error getting data source object id for a file", ex); 11560 return 0; 11561 } 11562 } 11563 11564 /** 11565 * Get last (max) object id of content object in tsk_objects. 11566 * 11567 * @return currently max id 11568 * 11569 * @throws TskCoreException exception thrown when database error occurs and 11570 * last object id could not be queried 11571 * @deprecated Do not use, assumes a single-threaded, single-user case. 11572 */ 11573 @Deprecated 11574 public long getLastObjectId() throws TskCoreException { 11575 CaseDbConnection connection = connections.getConnection(); 11576 acquireSingleUserCaseReadLock(); 11577 ResultSet rs = null; 11578 try { 11579 // SELECT MAX(obj_id) AS max_obj_id FROM tsk_objects 11580 PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_MAX_OBJECT_ID); 11581 rs = connection.executeQuery(statement); 11582 long id = -1; 11583 if (rs.next()) { 11584 id = rs.getLong("max_obj_id"); 11585 } 11586 return id; 11587 } catch (SQLException e) { 11588 throw new TskCoreException("Error getting last object id", e); 11589 } finally { 11590 closeResultSet(rs); 11591 connection.close(); 11592 releaseSingleUserCaseReadLock(); 11593 } 11594 } 11595 11596 /** 11597 * Find and return list of files matching the specific Where clause. Use 11598 * findAllFilesWhere instead. It returns a more generic data type 11599 * 11600 * @param sqlWhereClause a SQL where clause appropriate for the desired 11601 * files (do not begin the WHERE clause with the word 11602 * WHERE!) 11603 * 11604 * @return a list of FsContent each of which satisfy the given WHERE clause 11605 * 11606 * @throws TskCoreException 11607 * @deprecated use SleuthkitCase.findAllFilesWhere() instead 11608 */ 11609 @Deprecated 11610 public List<FsContent> findFilesWhere(String sqlWhereClause) throws TskCoreException { 11611 CaseDbConnection connection = connections.getConnection(); 11612 acquireSingleUserCaseReadLock(); 11613 Statement s = null; 11614 ResultSet rs = null; 11615 try { 11616 s = connection.createStatement(); 11617 rs = connection.executeQuery(s, "SELECT * FROM tsk_files WHERE " + sqlWhereClause); //NON-NLS 11618 List<FsContent> results = new ArrayList<FsContent>(); 11619 List<AbstractFile> temp = resultSetToAbstractFiles(rs, connection); 11620 for (AbstractFile f : temp) { 11621 final TSK_DB_FILES_TYPE_ENUM type = f.getType(); 11622 if (type.equals(TskData.TSK_DB_FILES_TYPE_ENUM.FS)) { 11623 results.add((FsContent) f); 11624 } 11625 } 11626 return results; 11627 } catch (SQLException e) { 11628 throw new TskCoreException("SQLException thrown when calling 'SleuthkitCase.findFilesWhere().", e); 11629 } finally { 11630 closeResultSet(rs); 11631 closeStatement(s); 11632 connection.close(); 11633 releaseSingleUserCaseReadLock(); 11634 } 11635 } 11636 11637 /** 11638 * Get the artifact type id associated with an artifact type name. 11639 * 11640 * @param artifactTypeName An artifact type name. 11641 * 11642 * @return An artifact id or -1 if the attribute type does not exist. 11643 * 11644 * @throws TskCoreException If an error occurs accessing the case database. 11645 * 11646 * @deprecated Use getArtifactType instead 11647 */ 11648 @Deprecated 11649 public int getArtifactTypeID(String artifactTypeName) throws TskCoreException { 11650 CaseDbConnection connection = connections.getConnection(); 11651 acquireSingleUserCaseReadLock(); 11652 Statement s = null; 11653 ResultSet rs = null; 11654 try { 11655 s = connection.createStatement(); 11656 rs = connection.executeQuery(s, "SELECT artifact_type_id FROM blackboard_artifact_types WHERE type_name = '" + artifactTypeName + "'"); //NON-NLS 11657 int typeId = -1; 11658 if (rs.next()) { 11659 typeId = rs.getInt("artifact_type_id"); 11660 } 11661 return typeId; 11662 } catch (SQLException ex) { 11663 throw new TskCoreException("Error getting artifact type id", ex); 11664 } finally { 11665 closeResultSet(rs); 11666 closeStatement(s); 11667 connection.close(); 11668 releaseSingleUserCaseReadLock(); 11669 } 11670 } 11671 11672 /** 11673 * Gets a list of the standard blackboard artifact type enum objects. 11674 * 11675 * @return The members of the BlackboardArtifact.ARTIFACT_TYPE enum. 11676 * 11677 * @throws TskCoreException Specified, but not thrown. 11678 * @deprecated For a list of standard blackboard artifacts type enum 11679 * objects, use BlackboardArtifact.ARTIFACT_TYPE.values. 11680 */ 11681 @Deprecated 11682 public ArrayList<BlackboardArtifact.ARTIFACT_TYPE> getBlackboardArtifactTypes() throws TskCoreException { 11683 return new ArrayList<BlackboardArtifact.ARTIFACT_TYPE>(Arrays.asList(BlackboardArtifact.ARTIFACT_TYPE.values())); 11684 } 11685 11686 /** 11687 * Adds a custom artifact type. The artifact type name must be unique, but 11688 * the display name need not be unique. 11689 * 11690 * @param artifactTypeName The artifact type name. 11691 * @param displayName The artifact type display name. 11692 * 11693 * @return The artifact type id assigned to the artifact type. 11694 * 11695 * @throws TskCoreException If there is an error adding the type to the case 11696 * database. 11697 * @deprecated Use SleuthkitCase.addBlackboardArtifactType instead. 11698 */ 11699 @Deprecated 11700 public int addArtifactType(String artifactTypeName, String displayName) throws TskCoreException { 11701 try { 11702 return addBlackboardArtifactType(artifactTypeName, displayName).getTypeID(); 11703 } catch (TskDataException ex) { 11704 throw new TskCoreException("Failed to add artifact type.", ex); 11705 } 11706 } 11707 11708 /** 11709 * Adds a custom attribute type with a string value type. The attribute type 11710 * name must be unique, but the display name need not be unique. 11711 * 11712 * @param attrTypeString The attribute type name. 11713 * @param displayName The attribute type display name. 11714 * 11715 * @return The attribute type id. 11716 * 11717 * @throws TskCoreException If there is an error adding the type to the case 11718 * database. 11719 * @deprecated Use SleuthkitCase.addArtifactAttributeType instead. 11720 */ 11721 @Deprecated 11722 public int addAttrType(String attrTypeString, String displayName) throws TskCoreException { 11723 try { 11724 return addArtifactAttributeType(attrTypeString, TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING, displayName).getTypeID(); 11725 } catch (TskDataException ex) { 11726 throw new TskCoreException("Couldn't add new attribute type"); 11727 } 11728 } 11729 11730 /** 11731 * Gets the attribute type id associated with an attribute type name. 11732 * 11733 * @param attrTypeName An attribute type name. 11734 * 11735 * @return An attribute id or -1 if the attribute type does not exist. 11736 * 11737 * @throws TskCoreException If an error occurs accessing the case database. 11738 * @deprecated Use SleuthkitCase.getAttributeType instead. 11739 */ 11740 @Deprecated 11741 public int getAttrTypeID(String attrTypeName) throws TskCoreException { 11742 CaseDbConnection connection = connections.getConnection(); 11743 acquireSingleUserCaseReadLock(); 11744 Statement s = null; 11745 ResultSet rs = null; 11746 try { 11747 s = connection.createStatement(); 11748 rs = connection.executeQuery(s, "SELECT attribute_type_id FROM blackboard_attribute_types WHERE type_name = '" + attrTypeName + "'"); //NON-NLS 11749 int typeId = -1; 11750 if (rs.next()) { 11751 typeId = rs.getInt("attribute_type_id"); 11752 } 11753 return typeId; 11754 } catch (SQLException ex) { 11755 throw new TskCoreException("Error getting attribute type id", ex); 11756 } finally { 11757 closeResultSet(rs); 11758 closeStatement(s); 11759 connection.close(); 11760 releaseSingleUserCaseReadLock(); 11761 } 11762 } 11763 11764 /** 11765 * Get the string associated with the given id. Will throw an error if that 11766 * id does not exist 11767 * 11768 * @param attrTypeID attribute id 11769 * 11770 * @return string associated with the given id 11771 * 11772 * @throws TskCoreException exception thrown if a critical error occurs 11773 * within tsk core 11774 * @deprecated Use getAttributeType instead 11775 */ 11776 @Deprecated 11777 public String getAttrTypeString(int attrTypeID) throws TskCoreException { 11778 CaseDbConnection connection = connections.getConnection(); 11779 acquireSingleUserCaseReadLock(); 11780 Statement s = null; 11781 ResultSet rs = null; 11782 try { 11783 s = connection.createStatement(); 11784 rs = connection.executeQuery(s, "SELECT type_name FROM blackboard_attribute_types WHERE attribute_type_id = " + attrTypeID); //NON-NLS 11785 if (rs.next()) { 11786 return rs.getString("type_name"); 11787 } else { 11788 throw new TskCoreException("No type with that id"); 11789 } 11790 } catch (SQLException ex) { 11791 throw new TskCoreException("Error getting or creating a attribute type name", ex); 11792 } finally { 11793 closeResultSet(rs); 11794 closeStatement(s); 11795 connection.close(); 11796 releaseSingleUserCaseReadLock(); 11797 } 11798 } 11799 11800 /** 11801 * Get the display name for the attribute with the given id. Will throw an 11802 * error if that id does not exist 11803 * 11804 * @param attrTypeID attribute id 11805 * 11806 * @return string associated with the given id 11807 * 11808 * @throws TskCoreException exception thrown if a critical error occurs 11809 * within tsk core 11810 * @deprecated Use getAttributeType instead 11811 */ 11812 @Deprecated 11813 public String getAttrTypeDisplayName(int attrTypeID) throws TskCoreException { 11814 CaseDbConnection connection = connections.getConnection(); 11815 acquireSingleUserCaseReadLock(); 11816 Statement s = null; 11817 ResultSet rs = null; 11818 try { 11819 s = connection.createStatement(); 11820 rs = connection.executeQuery(s, "SELECT display_name FROM blackboard_attribute_types WHERE attribute_type_id = " + attrTypeID); //NON-NLS 11821 if (rs.next()) { 11822 return rs.getString("display_name"); 11823 } else { 11824 throw new TskCoreException("No type with that id"); 11825 } 11826 } catch (SQLException ex) { 11827 throw new TskCoreException("Error getting or creating a attribute type name", ex); 11828 } finally { 11829 closeResultSet(rs); 11830 closeStatement(s); 11831 connection.close(); 11832 releaseSingleUserCaseReadLock(); 11833 } 11834 } 11835 11836 /** 11837 * Gets a list of the standard blackboard attribute type enum objects. 11838 * 11839 * @return The members of the BlackboardAttribute.ATTRIBUTE_TYPE enum. 11840 * 11841 * @throws TskCoreException Specified, but not thrown. 11842 * @deprecated For a list of standard blackboard attribute types enum 11843 * objects, use BlackboardAttribute.ATTRIBUTE_TYP.values. 11844 */ 11845 @Deprecated 11846 public ArrayList<BlackboardAttribute.ATTRIBUTE_TYPE> getBlackboardAttributeTypes() throws TskCoreException { 11847 return new ArrayList<BlackboardAttribute.ATTRIBUTE_TYPE>(Arrays.asList(BlackboardAttribute.ATTRIBUTE_TYPE.values())); 11848 } 11849 11850 /** 11851 * Process a read-only query on the tsk database, any table Can be used to 11852 * e.g. to find files of a given criteria. resultSetToFsContents() will 11853 * convert the files to useful objects. MUST CALL closeRunQuery() when done 11854 * 11855 * @param query the given string query to run 11856 * 11857 * @return the resultSet from running the query. Caller MUST CALL 11858 * closeRunQuery(resultSet) as soon as possible, when done with 11859 * retrieving data from the resultSet 11860 * 11861 * @throws SQLException if error occurred during the query 11862 * @deprecated Do not use runQuery(), use executeQuery() instead. \ref 11863 * query_database_page 11864 */ 11865 @Deprecated 11866 public ResultSet runQuery(String query) throws SQLException { 11867 CaseDbConnection connection; 11868 try { 11869 connection = connections.getConnection(); 11870 } catch (TskCoreException ex) { 11871 throw new SQLException("Error getting connection for ad hoc query", ex); 11872 } 11873 acquireSingleUserCaseReadLock(); 11874 try { 11875 return connection.executeQuery(connection.createStatement(), query); 11876 } finally { 11877 //TODO unlock should be done in closeRunQuery() 11878 //but currently not all code calls closeRunQuery - need to fix this 11879 connection.close(); 11880 releaseSingleUserCaseReadLock(); 11881 } 11882 } 11883 11884 /** 11885 * Closes ResultSet and its Statement previously retrieved from runQuery() 11886 * 11887 * @param resultSet with its Statement to close 11888 * 11889 * @throws SQLException of closing the query files failed 11890 * @deprecated Do not use runQuery() and closeRunQuery(), use executeQuery() 11891 * instead. \ref query_database_page 11892 */ 11893 @Deprecated 11894 public void closeRunQuery(ResultSet resultSet) throws SQLException { 11895 final Statement statement = resultSet.getStatement(); 11896 resultSet.close(); 11897 if (statement != null) { 11898 statement.close(); 11899 } 11900 } 11901 11902 /** 11903 * Adds a carved file to the VirtualDirectory '$CarvedFiles' in the volume 11904 * or image given by systemId. Creates $CarvedFiles virtual directory if it 11905 * does not exist already. 11906 * 11907 * @param carvedFileName the name of the carved file to add 11908 * @param carvedFileSize the size of the carved file to add 11909 * @param containerId the ID of the parent volume, file system, or image 11910 * @param data the layout information - a list of offsets that 11911 * make up this carved file. 11912 * 11913 * @return A LayoutFile object representing the carved file. 11914 * 11915 * @throws org.sleuthkit.datamodel.TskCoreException 11916 * @deprecated Use addCarvedFile(CarvingResult) instead 11917 */ 11918 @Deprecated 11919 public LayoutFile addCarvedFile(String carvedFileName, long carvedFileSize, long containerId, List<TskFileRange> data) throws TskCoreException { 11920 CarvingResult.CarvedFile carvedFile = new CarvingResult.CarvedFile(carvedFileName, carvedFileSize, data); 11921 List<CarvingResult.CarvedFile> files = new ArrayList<CarvingResult.CarvedFile>(); 11922 files.add(carvedFile); 11923 CarvingResult carvingResult; 11924 Content parent = getContentById(containerId); 11925 if (parent instanceof FileSystem 11926 || parent instanceof Volume 11927 || parent instanceof Image) { 11928 carvingResult = new CarvingResult(parent, files); 11929 } else { 11930 throw new TskCoreException(String.format("Parent (id =%d) is not an file system, volume or image", containerId)); 11931 } 11932 return addCarvedFiles(carvingResult).get(0); 11933 } 11934 11935 /** 11936 * Adds a collection of carved files to the VirtualDirectory '$CarvedFiles' 11937 * in the volume or image given by systemId. Creates $CarvedFiles virtual 11938 * directory if it does not exist already. 11939 * 11940 * @param filesToAdd A list of CarvedFileContainer files to add as carved 11941 * files. 11942 * 11943 * @return A list of the files added to the database. 11944 * 11945 * @throws org.sleuthkit.datamodel.TskCoreException 11946 * @deprecated Use addCarvedFile(CarvingResult) instead 11947 */ 11948 @Deprecated 11949 public List<LayoutFile> addCarvedFiles(List<CarvedFileContainer> filesToAdd) throws TskCoreException { 11950 List<CarvingResult.CarvedFile> carvedFiles = new ArrayList<CarvingResult.CarvedFile>(); 11951 for (CarvedFileContainer container : filesToAdd) { 11952 CarvingResult.CarvedFile carvedFile = new CarvingResult.CarvedFile(container.getName(), container.getSize(), container.getRanges()); 11953 carvedFiles.add(carvedFile); 11954 } 11955 CarvingResult carvingResult; 11956 Content parent = getContentById(filesToAdd.get(0).getId()); 11957 if (parent instanceof FileSystem 11958 || parent instanceof Volume 11959 || parent instanceof Image) { 11960 carvingResult = new CarvingResult(parent, carvedFiles); 11961 } else { 11962 throw new TskCoreException(String.format("Parent (id =%d) is not an file system, volume or image", parent.getId())); 11963 } 11964 return addCarvedFiles(carvingResult); 11965 } 11966 11967 /** 11968 * Creates a new derived file object, adds it to database and returns it. 11969 * 11970 * TODO add support for adding derived method 11971 * 11972 * @param fileName file name the derived file 11973 * @param localPath local path of the derived file, including the file 11974 * name. The path is relative to the database path. 11975 * @param size size of the derived file in bytes 11976 * @param ctime The changed time of the file. 11977 * @param crtime The creation time of the file. 11978 * @param atime The accessed time of the file 11979 * @param mtime The modified time of the file. 11980 * @param isFile whether a file or directory, true if a file 11981 * @param parentFile parent file object (derived or local file) 11982 * @param rederiveDetails details needed to re-derive file (will be specific 11983 * to the derivation method), currently unused 11984 * @param toolName name of derivation method/tool, currently unused 11985 * @param toolVersion version of derivation method/tool, currently 11986 * unused 11987 * @param otherDetails details of derivation method/tool, currently 11988 * unused 11989 * 11990 * @return newly created derived file object 11991 * 11992 * @throws TskCoreException exception thrown if the object creation failed 11993 * due to a critical system error 11994 * @deprecated Use the newer version with explicit encoding type parameter 11995 */ 11996 @Deprecated 11997 public DerivedFile addDerivedFile(String fileName, String localPath, 11998 long size, long ctime, long crtime, long atime, long mtime, 11999 boolean isFile, AbstractFile parentFile, 12000 String rederiveDetails, String toolName, String toolVersion, String otherDetails) throws TskCoreException { 12001 return addDerivedFile(fileName, localPath, size, ctime, crtime, atime, mtime, 12002 isFile, parentFile, rederiveDetails, toolName, toolVersion, 12003 otherDetails, TskData.EncodingType.NONE); 12004 } 12005 12006 /** 12007 * Adds a local/logical file to the case database. The database operations 12008 * are done within a caller-managed transaction; the caller is responsible 12009 * for committing or rolling back the transaction. 12010 * 12011 * @param fileName The name of the file. 12012 * @param localPath The absolute path (including the file name) of the 12013 * local/logical in secondary storage. 12014 * @param size The size of the file in bytes. 12015 * @param ctime The changed time of the file. 12016 * @param crtime The creation time of the file. 12017 * @param atime The accessed time of the file 12018 * @param mtime The modified time of the file. 12019 * @param isFile True, unless the file is a directory. 12020 * @param parent The parent of the file (e.g., a virtual directory) 12021 * @param transaction A caller-managed transaction within which the add file 12022 * operations are performed. 12023 * 12024 * @return An object representing the local/logical file. 12025 * 12026 * @throws TskCoreException if there is an error completing a case database 12027 * operation. 12028 * @deprecated Use the newer version with explicit encoding type parameter 12029 */ 12030 @Deprecated 12031 public LocalFile addLocalFile(String fileName, String localPath, 12032 long size, long ctime, long crtime, long atime, long mtime, 12033 boolean isFile, 12034 AbstractFile parent, CaseDbTransaction transaction) throws TskCoreException { 12035 return addLocalFile(fileName, localPath, size, ctime, crtime, atime, mtime, isFile, 12036 TskData.EncodingType.NONE, parent, transaction); 12037 } 12038 12039 /** 12040 * Wraps the version of addLocalFile that takes a Transaction in a 12041 * transaction local to this method. 12042 * 12043 * @param fileName 12044 * @param localPath 12045 * @param size 12046 * @param ctime 12047 * @param crtime 12048 * @param atime 12049 * @param mtime 12050 * @param isFile 12051 * @param parent 12052 * 12053 * @return 12054 * 12055 * @throws TskCoreException 12056 * @deprecated Use the newer version with explicit encoding type parameter 12057 */ 12058 @Deprecated 12059 public LocalFile addLocalFile(String fileName, String localPath, 12060 long size, long ctime, long crtime, long atime, long mtime, 12061 boolean isFile, 12062 AbstractFile parent) throws TskCoreException { 12063 return addLocalFile(fileName, localPath, size, ctime, crtime, atime, mtime, 12064 isFile, TskData.EncodingType.NONE, parent); 12065 } 12066 12067 /** 12068 * Start process of adding a image to the case. Adding an image is a 12069 * multi-step process and this returns an object that allows it to happen. 12070 * 12071 * @param timezone TZ time zone string to use for ingest of image. 12072 * @param addUnallocSpace Set to true to create virtual files for 12073 * unallocated space in the image. 12074 * @param noFatFsOrphans Set to true to skip processing orphan files of FAT 12075 * file systems. 12076 * 12077 * @return Object that encapsulates control of adding an image via the 12078 * SleuthKit native code layer 12079 * 12080 * @deprecated Use the newer version with explicit image writer path 12081 * parameter 12082 */ 12083 @Deprecated 12084 public AddImageProcess makeAddImageProcess(String timezone, boolean addUnallocSpace, boolean noFatFsOrphans) { 12085 return this.caseHandle.initAddImageProcess(timezone, addUnallocSpace, noFatFsOrphans, "", this); 12086 } 12087 12088 /** 12089 * Acquires a write lock, but only if this is a single-user case. Always 12090 * call this method in a try block with a call to the lock release method in 12091 * an associated finally block. 12092 * 12093 * @deprecated Use acquireSingleUserCaseWriteLock. 12094 */ 12095 @Deprecated 12096 public void acquireExclusiveLock() { 12097 acquireSingleUserCaseWriteLock(); 12098 } 12099 12100 /** 12101 * Releases a write lock, but only if this is a single-user case. This 12102 * method should always be called in the finally block of a try block in 12103 * which the lock was acquired. 12104 * 12105 * @deprecated Use releaseSingleUserCaseWriteLock. 12106 */ 12107 @Deprecated 12108 public void releaseExclusiveLock() { 12109 releaseSingleUserCaseWriteLock(); 12110 } 12111 12112 /** 12113 * Acquires a read lock, but only if this is a single-user case. Call this 12114 * method in a try block with a call to the lock release method in an 12115 * associated finally block. 12116 * 12117 * @deprecated Use acquireSingleUserCaseReadLock. 12118 */ 12119 @Deprecated 12120 public void acquireSharedLock() { 12121 acquireSingleUserCaseReadLock(); 12122 } 12123 12124 /** 12125 * Releases a read lock, but only if this is a single-user case. This method 12126 * should always be called in the finally block of a try block in which the 12127 * lock was acquired. 12128 * 12129 * @deprecated Use releaseSingleUserCaseReadLock. 12130 */ 12131 @Deprecated 12132 public void releaseSharedLock() { 12133 releaseSingleUserCaseReadLock(); 12134 } 12135 }; 12136