19 package org.sleuthkit.autopsy.centralrepository.datamodel;
 
   22 import java.io.IOException;
 
   23 import java.nio.file.Files;
 
   24 import java.nio.file.InvalidPathException;
 
   25 import java.sql.Connection;
 
   26 import java.sql.DriverManager;
 
   27 import java.sql.SQLException;
 
   28 import java.sql.Statement;
 
   29 import java.util.List;
 
   30 import java.util.logging.Level;
 
   31 import java.util.regex.Pattern;
 
   69         if (dbName == null || dbName.isEmpty()) {
 
   74         if (dbDirectory == null || dbDirectory.isEmpty()) {
 
   80             if (bulkThresholdString == null || bulkThresholdString.isEmpty()) {
 
   81                 this.bulkThreshold = AbstractSqlEamDb.DEFAULT_BULK_THRESHHOLD;
 
   83                 this.bulkThreshold = Integer.parseInt(bulkThresholdString);
 
   84                 if (getBulkThreshold() <= 0) {
 
   85                     this.bulkThreshold = AbstractSqlEamDb.DEFAULT_BULK_THRESHHOLD;
 
   88         } 
catch (NumberFormatException ex) {
 
   89             this.bulkThreshold = AbstractSqlEamDb.DEFAULT_BULK_THRESHHOLD;
 
  108         if (!dbFile.exists()) {
 
  112         return (!dbFile.isDirectory());
 
  124         if (!dbDir.exists()) {
 
  126         } 
else if (!dbDir.isDirectory()) {
 
  143                 Files.createDirectories(dbDir.toPath());
 
  144                 LOGGER.log(Level.INFO, 
"sqlite directory did not exist, created it at {0}.", 
getDbDirectory()); 
 
  145             } 
catch (IOException | InvalidPathException | SecurityException ex) {
 
  146                 LOGGER.log(Level.SEVERE, 
"Failed to create sqlite database directory.", ex); 
 
  161         return dbFile.delete();
 
  169     String getConnectionURL() {
 
  170         StringBuilder url = 
new StringBuilder();
 
  171         url.append(getJDBCBaseURI());
 
  174         return url.toString();
 
  192             String url = getConnectionURL();
 
  193             Class.forName(getDriver());
 
  194             conn = DriverManager.getConnection(url);
 
  195         } 
catch (ClassNotFoundException | SQLException ex) {
 
  196             LOGGER.log(Level.SEVERE, 
"Failed to acquire ephemeral connection to sqlite.", ex); 
 
  253         StringBuilder createOrganizationsTable = 
new StringBuilder();
 
  254         createOrganizationsTable.append(
"CREATE TABLE IF NOT EXISTS organizations (");
 
  255         createOrganizationsTable.append(
"id integer primary key autoincrement NOT NULL,");
 
  256         createOrganizationsTable.append(
"org_name text NOT NULL,");
 
  257         createOrganizationsTable.append(
"poc_name text NOT NULL,");
 
  258         createOrganizationsTable.append(
"poc_email text NOT NULL,");
 
  259         createOrganizationsTable.append(
"poc_phone text NOT NULL,");
 
  260         createOrganizationsTable.append(
"CONSTRAINT org_name_unique UNIQUE (org_name)");
 
  261         createOrganizationsTable.append(
")");
 
  265         StringBuilder createCasesTable = 
new StringBuilder();
 
  266         createCasesTable.append(
"CREATE TABLE IF NOT EXISTS cases (");
 
  267         createCasesTable.append(
"id integer primary key autoincrement NOT NULL,");
 
  268         createCasesTable.append(
"case_uid text NOT NULL,");
 
  269         createCasesTable.append(
"org_id integer,");
 
  270         createCasesTable.append(
"case_name text NOT NULL,");
 
  271         createCasesTable.append(
"creation_date text NOT NULL,");
 
  272         createCasesTable.append(
"case_number text,");
 
  273         createCasesTable.append(
"examiner_name text,");
 
  274         createCasesTable.append(
"examiner_email text,");
 
  275         createCasesTable.append(
"examiner_phone text,");
 
  276         createCasesTable.append(
"notes text,");
 
  277         createCasesTable.append(
"CONSTRAINT case_uid_unique UNIQUE(case_uid) ON CONFLICT IGNORE,");
 
  278         createCasesTable.append(
"foreign key (org_id) references organizations(id) ON UPDATE SET NULL ON DELETE SET NULL");
 
  279         createCasesTable.append(
")");
 
  282         String casesIdx1 = 
"CREATE INDEX IF NOT EXISTS cases_org_id ON cases (org_id)";
 
  283         String casesIdx2 = 
"CREATE INDEX IF NOT EXISTS cases_case_uid ON cases (case_uid)";
 
  285         StringBuilder createReferenceSetsTable = 
new StringBuilder();
 
  286         createReferenceSetsTable.append(
"CREATE TABLE IF NOT EXISTS reference_sets (");
 
  287         createReferenceSetsTable.append(
"id integer primary key autoincrement NOT NULL,");
 
  288         createReferenceSetsTable.append(
"org_id integer NOT NULL,");
 
  289         createReferenceSetsTable.append(
"set_name text NOT NULL,");
 
  290         createReferenceSetsTable.append(
"version text NOT NULL,");
 
  291         createReferenceSetsTable.append(
"known_status integer NOT NULL,");
 
  292         createReferenceSetsTable.append(
"read_only boolean NOT NULL,");
 
  293         createReferenceSetsTable.append(
"type integer NOT NULL,");
 
  294         createReferenceSetsTable.append(
"import_date text NOT NULL,");
 
  295         createReferenceSetsTable.append(
"foreign key (org_id) references organizations(id) ON UPDATE SET NULL ON DELETE SET NULL,");
 
  296         createReferenceSetsTable.append(
"CONSTRAINT hash_set_unique UNIQUE (set_name, version)");
 
  297         createReferenceSetsTable.append(
")");
 
  299         String referenceSetsIdx1 = 
"CREATE INDEX IF NOT EXISTS reference_sets_org_id ON reference_sets (org_id)";
 
  302         StringBuilder createReferenceTypesTableTemplate = 
new StringBuilder();
 
  303         createReferenceTypesTableTemplate.append(
"CREATE TABLE IF NOT EXISTS %s (");
 
  304         createReferenceTypesTableTemplate.append(
"id integer primary key autoincrement NOT NULL,");
 
  305         createReferenceTypesTableTemplate.append(
"reference_set_id integer,");
 
  306         createReferenceTypesTableTemplate.append(
"value text NOT NULL,");
 
  307         createReferenceTypesTableTemplate.append(
"known_status integer NOT NULL,");
 
  308         createReferenceTypesTableTemplate.append(
"comment text,");
 
  309         createReferenceTypesTableTemplate.append(
"CONSTRAINT %s_multi_unique UNIQUE(reference_set_id, value) ON CONFLICT IGNORE,");
 
  310         createReferenceTypesTableTemplate.append(
"foreign key (reference_set_id) references reference_sets(id) ON UPDATE SET NULL ON DELETE SET NULL");
 
  311         createReferenceTypesTableTemplate.append(
")");
 
  314         String referenceTypesIdx1 = 
"CREATE INDEX IF NOT EXISTS %s_value ON %s (value)";
 
  315         String referenceTypesIdx2 = 
"CREATE INDEX IF NOT EXISTS %s_value_known_status ON %s (value, known_status)";
 
  317         StringBuilder createCorrelationTypesTable = 
new StringBuilder();
 
  318         createCorrelationTypesTable.append(
"CREATE TABLE IF NOT EXISTS correlation_types (");
 
  319         createCorrelationTypesTable.append(
"id integer primary key autoincrement NOT NULL,");
 
  320         createCorrelationTypesTable.append(
"display_name text NOT NULL,");
 
  321         createCorrelationTypesTable.append(
"db_table_name text NOT NULL,");
 
  322         createCorrelationTypesTable.append(
"supported integer NOT NULL,");
 
  323         createCorrelationTypesTable.append(
"enabled integer NOT NULL,");
 
  324         createCorrelationTypesTable.append(
"CONSTRAINT correlation_types_names UNIQUE (display_name, db_table_name)");
 
  325         createCorrelationTypesTable.append(
")");
 
  327         String createArtifactInstancesTableTemplate = getCreateArtifactInstancesTableTemplate();
 
  329         String instancesCaseIdIdx = getAddCaseIdIndexTemplate();
 
  330         String instancesDatasourceIdIdx = getAddDataSourceIdIndexTemplate();
 
  331         String instancesValueIdx = getAddValueIndexTemplate();
 
  332         String instancesKnownStatusIdx = getAddKnownStatusIndexTemplate();
 
  333         String instancesObjectIdIdx = getAddObjectIdIndexTemplate();
 
  337         Connection conn = null;
 
  343             Statement stmt = conn.createStatement();
 
  344             stmt.execute(PRAGMA_JOURNAL_WAL);
 
  345             stmt.execute(PRAGMA_SYNC_OFF);
 
  346             stmt.execute(PRAGMA_READ_UNCOMMITTED_TRUE);
 
  347             stmt.execute(PRAGMA_ENCODING_UTF8);
 
  348             stmt.execute(PRAGMA_PAGE_SIZE_4096);
 
  349             stmt.execute(PRAGMA_FOREIGN_KEYS_ON);
 
  351             stmt.execute(createOrganizationsTable.toString());
 
  353             stmt.execute(createCasesTable.toString());
 
  354             stmt.execute(casesIdx1);
 
  355             stmt.execute(casesIdx2);
 
  357             stmt.execute(getCreateDataSourcesTableStatement());
 
  358             stmt.execute(getAddDataSourcesNameIndexStatement());
 
  359             stmt.execute(getAddDataSourcesObjectIdIndexStatement());
 
  361             stmt.execute(createReferenceSetsTable.toString());
 
  362             stmt.execute(referenceSetsIdx1);
 
  364             stmt.execute(createCorrelationTypesTable.toString());
 
  371             stmt.execute(
"CREATE TABLE db_info (id INTEGER PRIMARY KEY, name TEXT UNIQUE NOT NULL, value TEXT NOT NULL)");
 
  372             stmt.execute(
"INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.SCHEMA_MAJOR_VERSION_KEY + 
"', '" + SOFTWARE_CR_DB_SCHEMA_VERSION.getMajor() + 
"')");
 
  373             stmt.execute(
"INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.SCHEMA_MINOR_VERSION_KEY + 
"', '" + SOFTWARE_CR_DB_SCHEMA_VERSION.getMinor() + 
"')");
 
  374             stmt.execute(
"INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.CREATION_SCHEMA_MAJOR_VERSION_KEY + 
"', '" + SOFTWARE_CR_DB_SCHEMA_VERSION.getMajor() + 
"')");
 
  375             stmt.execute(
"INSERT INTO db_info (name, value) VALUES ('" + AbstractSqlEamDb.CREATION_SCHEMA_MINOR_VERSION_KEY + 
"', '" + SOFTWARE_CR_DB_SCHEMA_VERSION.getMinor() + 
"')");
 
  380             String reference_type_dbname;
 
  381             String instance_type_dbname;
 
  386                 stmt.execute(String.format(createArtifactInstancesTableTemplate, instance_type_dbname, instance_type_dbname));
 
  387                 stmt.execute(String.format(instancesCaseIdIdx, instance_type_dbname, instance_type_dbname));
 
  388                 stmt.execute(String.format(instancesDatasourceIdIdx, instance_type_dbname, instance_type_dbname));
 
  389                 stmt.execute(String.format(instancesValueIdx, instance_type_dbname, instance_type_dbname));
 
  390                 stmt.execute(String.format(instancesKnownStatusIdx, instance_type_dbname, instance_type_dbname));
 
  391                 stmt.execute(String.format(instancesObjectIdIdx, instance_type_dbname, instance_type_dbname));
 
  395                     stmt.execute(String.format(createReferenceTypesTableTemplate.toString(), reference_type_dbname, reference_type_dbname));
 
  396                     stmt.execute(String.format(referenceTypesIdx1, reference_type_dbname, reference_type_dbname));
 
  397                     stmt.execute(String.format(referenceTypesIdx2, reference_type_dbname, reference_type_dbname));
 
  400         } 
catch (SQLException ex) {
 
  401             LOGGER.log(Level.SEVERE, 
"Error initializing db schema.", ex); 
 
  404             LOGGER.log(Level.SEVERE, 
"Error getting default correlation types. Likely due to one or more Type's with an invalid db table name."); 
 
  419     static String getCreateArtifactInstancesTableTemplate() {
 
  421         return "CREATE TABLE IF NOT EXISTS %s (id integer primary key autoincrement NOT NULL," 
  422                 + 
"case_id integer NOT NULL,data_source_id integer NOT NULL,value text NOT NULL," 
  423                 + 
"file_path text NOT NULL,known_status integer NOT NULL,comment text,file_obj_id integer," 
  424                 + 
"CONSTRAINT %s_multi_unique UNIQUE(data_source_id, value, file_path) ON CONFLICT IGNORE," 
  425                 + 
"foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL," 
  426                 + 
"foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL)";
 
  436     static String getCreateDataSourcesTableStatement() {
 
  437         return "CREATE TABLE IF NOT EXISTS data_sources (id integer primary key autoincrement NOT NULL," 
  438                 + 
"case_id integer NOT NULL,device_id text NOT NULL,name text NOT NULL,datasource_obj_id integer," 
  439                 + 
"md5 text DEFAULT NULL,sha1 text DEFAULT NULL,sha256 text DEFAULT NULL," 
  440                 + 
"foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL," 
  441                 + 
"CONSTRAINT datasource_unique UNIQUE (case_id, datasource_obj_id))";
 
  451     static String getAddDataSourcesNameIndexStatement() {
 
  452         return "CREATE INDEX IF NOT EXISTS data_sources_name ON data_sources (name)";
 
  462     static String getAddDataSourcesObjectIdIndexStatement() {
 
  463         return "CREATE INDEX IF NOT EXISTS data_sources_object_id ON data_sources (datasource_obj_id)";
 
  474     static String getAddCaseIdIndexTemplate() {
 
  476         return "CREATE INDEX IF NOT EXISTS %s_case_id ON %s (case_id)";
 
  487     static String getAddDataSourceIdIndexTemplate() {
 
  489         return "CREATE INDEX IF NOT EXISTS %s_data_source_id ON %s (data_source_id)";
 
  500     static String getAddValueIndexTemplate() {
 
  502         return "CREATE INDEX IF NOT EXISTS %s_value ON %s (value)";
 
  513     static String getAddKnownStatusIndexTemplate() {
 
  515         return "CREATE INDEX IF NOT EXISTS %s_value_known_status ON %s (value, known_status)";
 
  526     static String getAddObjectIdIndexTemplate() {
 
  528         return "CREATE INDEX IF NOT EXISTS %s_file_obj_id ON %s (file_obj_id)";
 
  542     boolean isChanged() {
 
  547         return !dbName.equals(dbNameString)
 
  548                 || !dbDirectory.equals(dbDirectoryString)
 
  549                 || !Integer.toString(bulkThreshold).equals(bulkThresholdString);
 
  565         if (dbName == null || dbName.isEmpty()) {
 
  566             throw new EamDbException(
"Invalid database file name. Cannot be null or empty."); 
 
  567         } 
else if (!Pattern.matches(DB_NAMES_REGEX, dbName)) {
 
  568             throw new EamDbException(
"Invalid database file name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'."); 
 
  577     int getBulkThreshold() {
 
  585         if (bulkThreshold > 0) {
 
  588             throw new EamDbException(
"Invalid bulk threshold."); 
 
  607         if (dbDirectory != null && !dbDirectory.isEmpty()) {
 
  610             throw new EamDbException(
"Invalid directory for sqlite database. Cannot empty"); 
 
  633     String getValidationQuery() {
 
  640     String getJDBCBaseURI() {
 
boolean createDbDirectory()
boolean insertDefaultDatabaseContent()
static String correlationTypeToInstanceTableName(CorrelationAttributeInstance.Type type)
static boolean schemaVersionIsSet(Connection conn)
static final String PRAGMA_JOURNAL_WAL
static final String PRAGMA_SYNC_OFF
static boolean executeValidationQuery(Connection conn, String validationQuery)
String getFileNameWithPath()
static List< CorrelationAttributeInstance.Type > getDefaultCorrelationTypes()
boolean verifyConnection()
boolean initializeDatabaseSchema()
Connection getEphemeralConnection()
static final String VALIDATION_QUERY
static final String DEFAULT_DBDIRECTORY
static final String PRAGMA_READ_UNCOMMITTED_TRUE
static final String JDBC_BASE_URI
static void closeConnection(Connection conn)
static synchronized void setConfigSetting(String moduleName, String settingName, String settingVal)
boolean verifyDatabaseSchema()
static final String PRAGMA_PAGE_SIZE_4096
static boolean insertDefaultCorrelationTypes(Connection conn)
static final Logger LOGGER
static String getConfigSetting(String moduleName, String settingName)
static final String PRAGMA_SYNC_NORMAL
static final String DB_NAMES_REGEX
static final String DEFAULT_DBNAME
void setDbName(String dbName)
synchronized static Logger getLogger(String name)
static String correlationTypeToReferenceTableName(CorrelationAttributeInstance.Type type)
static final String PRAGMA_FOREIGN_KEYS_ON
static final String JDBC_DRIVER
static final String PRAGMA_ENCODING_UTF8
void setDbDirectory(String dbDirectory)
boolean dbDirectoryExists()
static final int FILES_TYPE_ID