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;
67 if (dbName == null || dbName.isEmpty()) {
72 if (dbDirectory == null || dbDirectory.isEmpty()) {
78 if (bulkThresholdString == null || bulkThresholdString.isEmpty()) {
79 this.bulkThreshold = AbstractSqlEamDb.DEFAULT_BULK_THRESHHOLD;
81 this.bulkThreshold = Integer.parseInt(bulkThresholdString);
82 if (getBulkThreshold() <= 0) {
83 this.bulkThreshold = AbstractSqlEamDb.DEFAULT_BULK_THRESHHOLD;
86 }
catch (NumberFormatException ex) {
87 this.bulkThreshold = AbstractSqlEamDb.DEFAULT_BULK_THRESHHOLD;
106 if(! dbFile.exists()){
110 return ( ! dbFile.isDirectory());
122 if (!dbDir.exists()) {
124 }
else if (!dbDir.isDirectory()) {
141 Files.createDirectories(dbDir.toPath());
142 LOGGER.log(Level.INFO,
"sqlite directory did not exist, created it at {0}.",
getDbDirectory());
143 }
catch (IOException | InvalidPathException | SecurityException ex) {
144 LOGGER.log(Level.SEVERE,
"Failed to create sqlite database directory.", ex);
158 return dbFile.delete();
166 String getConnectionURL() {
167 StringBuilder url =
new StringBuilder();
168 url.append(getJDBCBaseURI());
171 return url.toString();
189 String url = getConnectionURL();
190 Class.forName(getDriver());
191 conn = DriverManager.getConnection(url);
192 }
catch (ClassNotFoundException | SQLException ex) {
193 LOGGER.log(Level.SEVERE,
"Failed to acquire ephemeral connection to sqlite.", ex);
250 StringBuilder createOrganizationsTable =
new StringBuilder();
251 createOrganizationsTable.append(
"CREATE TABLE IF NOT EXISTS organizations (");
252 createOrganizationsTable.append(
"id integer primary key autoincrement NOT NULL,");
253 createOrganizationsTable.append(
"org_name text NOT NULL,");
254 createOrganizationsTable.append(
"poc_name text NOT NULL,");
255 createOrganizationsTable.append(
"poc_email text NOT NULL,");
256 createOrganizationsTable.append(
"poc_phone text NOT NULL,");
257 createOrganizationsTable.append(
"CONSTRAINT org_name_unique UNIQUE (org_name)");
258 createOrganizationsTable.append(
")");
262 StringBuilder createCasesTable =
new StringBuilder();
263 createCasesTable.append(
"CREATE TABLE IF NOT EXISTS cases (");
264 createCasesTable.append(
"id integer primary key autoincrement NOT NULL,");
265 createCasesTable.append(
"case_uid text NOT NULL,");
266 createCasesTable.append(
"org_id integer,");
267 createCasesTable.append(
"case_name text NOT NULL,");
268 createCasesTable.append(
"creation_date text NOT NULL,");
269 createCasesTable.append(
"case_number text,");
270 createCasesTable.append(
"examiner_name text,");
271 createCasesTable.append(
"examiner_email text,");
272 createCasesTable.append(
"examiner_phone text,");
273 createCasesTable.append(
"notes text,");
274 createCasesTable.append(
"CONSTRAINT case_uid_unique UNIQUE(case_uid) ON CONFLICT IGNORE,");
275 createCasesTable.append(
"foreign key (org_id) references organizations(id) ON UPDATE SET NULL ON DELETE SET NULL");
276 createCasesTable.append(
")");
279 String casesIdx1 =
"CREATE INDEX IF NOT EXISTS cases_org_id ON cases (org_id)";
280 String casesIdx2 =
"CREATE INDEX IF NOT EXISTS cases_case_uid ON cases (case_uid)";
282 StringBuilder createDataSourcesTable =
new StringBuilder();
283 createDataSourcesTable.append(
"CREATE TABLE IF NOT EXISTS data_sources (");
284 createDataSourcesTable.append(
"id integer primary key autoincrement NOT NULL,");
285 createDataSourcesTable.append(
"case_id integer NOT NULL,");
286 createDataSourcesTable.append(
"device_id text NOT NULL,");
287 createDataSourcesTable.append(
"name text NOT NULL,");
288 createDataSourcesTable.append(
"foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,");
289 createDataSourcesTable.append(
"CONSTRAINT datasource_unique UNIQUE (case_id, device_id, name)");
290 createDataSourcesTable.append(
")");
292 String dataSourceIdx1 =
"CREATE INDEX IF NOT EXISTS data_sources_name ON data_sources (name)";
294 StringBuilder createReferenceSetsTable =
new StringBuilder();
295 createReferenceSetsTable.append(
"CREATE TABLE IF NOT EXISTS reference_sets (");
296 createReferenceSetsTable.append(
"id integer primary key autoincrement NOT NULL,");
297 createReferenceSetsTable.append(
"org_id integer NOT NULL,");
298 createReferenceSetsTable.append(
"set_name text NOT NULL,");
299 createReferenceSetsTable.append(
"version text NOT NULL,");
300 createReferenceSetsTable.append(
"known_status integer NOT NULL,");
301 createReferenceSetsTable.append(
"read_only boolean NOT NULL,");
302 createReferenceSetsTable.append(
"type integer NOT NULL,");
303 createReferenceSetsTable.append(
"import_date text NOT NULL,");
304 createReferenceSetsTable.append(
"foreign key (org_id) references organizations(id) ON UPDATE SET NULL ON DELETE SET NULL,");
305 createReferenceSetsTable.append(
"CONSTRAINT hash_set_unique UNIQUE (set_name, version)");
306 createReferenceSetsTable.append(
")");
308 String referenceSetsIdx1 =
"CREATE INDEX IF NOT EXISTS reference_sets_org_id ON reference_sets (org_id)";
311 StringBuilder createReferenceTypesTableTemplate =
new StringBuilder();
312 createReferenceTypesTableTemplate.append(
"CREATE TABLE IF NOT EXISTS %s (");
313 createReferenceTypesTableTemplate.append(
"id integer primary key autoincrement NOT NULL,");
314 createReferenceTypesTableTemplate.append(
"reference_set_id integer,");
315 createReferenceTypesTableTemplate.append(
"value text NOT NULL,");
316 createReferenceTypesTableTemplate.append(
"known_status integer NOT NULL,");
317 createReferenceTypesTableTemplate.append(
"comment text,");
318 createReferenceTypesTableTemplate.append(
"CONSTRAINT %s_multi_unique UNIQUE(reference_set_id, value) ON CONFLICT IGNORE,");
319 createReferenceTypesTableTemplate.append(
"foreign key (reference_set_id) references reference_sets(id) ON UPDATE SET NULL ON DELETE SET NULL");
320 createReferenceTypesTableTemplate.append(
")");
323 String referenceTypesIdx1 =
"CREATE INDEX IF NOT EXISTS %s_value ON %s (value)";
324 String referenceTypesIdx2 =
"CREATE INDEX IF NOT EXISTS %s_value_known_status ON %s (value, known_status)";
326 StringBuilder createCorrelationTypesTable =
new StringBuilder();
327 createCorrelationTypesTable.append(
"CREATE TABLE IF NOT EXISTS correlation_types (");
328 createCorrelationTypesTable.append(
"id integer primary key autoincrement NOT NULL,");
329 createCorrelationTypesTable.append(
"display_name text NOT NULL,");
330 createCorrelationTypesTable.append(
"db_table_name text NOT NULL,");
331 createCorrelationTypesTable.append(
"supported integer NOT NULL,");
332 createCorrelationTypesTable.append(
"enabled integer NOT NULL,");
333 createCorrelationTypesTable.append(
"CONSTRAINT correlation_types_names UNIQUE (display_name, db_table_name)");
334 createCorrelationTypesTable.append(
")");
337 StringBuilder createArtifactInstancesTableTemplate =
new StringBuilder();
338 createArtifactInstancesTableTemplate.append(
"CREATE TABLE IF NOT EXISTS %s (");
339 createArtifactInstancesTableTemplate.append(
"id integer primary key autoincrement NOT NULL,");
340 createArtifactInstancesTableTemplate.append(
"case_id integer NOT NULL,");
341 createArtifactInstancesTableTemplate.append(
"data_source_id integer NOT NULL,");
342 createArtifactInstancesTableTemplate.append(
"value text NOT NULL,");
343 createArtifactInstancesTableTemplate.append(
"file_path text NOT NULL,");
344 createArtifactInstancesTableTemplate.append(
"known_status integer NOT NULL,");
345 createArtifactInstancesTableTemplate.append(
"comment text,");
346 createArtifactInstancesTableTemplate.append(
"CONSTRAINT %s_multi_unique UNIQUE(data_source_id, value, file_path) ON CONFLICT IGNORE,");
347 createArtifactInstancesTableTemplate.append(
"foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,");
348 createArtifactInstancesTableTemplate.append(
"foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL");
349 createArtifactInstancesTableTemplate.append(
")");
352 String instancesIdx1 =
"CREATE INDEX IF NOT EXISTS %s_case_id ON %s (case_id)";
353 String instancesIdx2 =
"CREATE INDEX IF NOT EXISTS %s_data_source_id ON %s (data_source_id)";
354 String instancesIdx3 =
"CREATE INDEX IF NOT EXISTS %s_value ON %s (value)";
355 String instancesIdx4 =
"CREATE INDEX IF NOT EXISTS %s_value_known_status ON %s (value, known_status)";
357 StringBuilder createDbInfoTable =
new StringBuilder();
358 createDbInfoTable.append(
"CREATE TABLE IF NOT EXISTS db_info (");
359 createDbInfoTable.append(
"id integer primary key NOT NULL,");
360 createDbInfoTable.append(
"name text NOT NULL,");
361 createDbInfoTable.append(
"value text NOT NULL");
362 createDbInfoTable.append(
")");
366 Connection conn = null;
372 Statement stmt = conn.createStatement();
373 stmt.execute(PRAGMA_JOURNAL_WAL);
374 stmt.execute(PRAGMA_SYNC_OFF);
375 stmt.execute(PRAGMA_READ_UNCOMMITTED_TRUE);
376 stmt.execute(PRAGMA_ENCODING_UTF8);
377 stmt.execute(PRAGMA_PAGE_SIZE_4096);
378 stmt.execute(PRAGMA_FOREIGN_KEYS_ON);
380 stmt.execute(createOrganizationsTable.toString());
382 stmt.execute(createCasesTable.toString());
383 stmt.execute(casesIdx1);
384 stmt.execute(casesIdx2);
386 stmt.execute(createDataSourcesTable.toString());
387 stmt.execute(dataSourceIdx1);
389 stmt.execute(createReferenceSetsTable.toString());
390 stmt.execute(referenceSetsIdx1);
392 stmt.execute(createCorrelationTypesTable.toString());
394 stmt.execute(createDbInfoTable.toString());
399 String reference_type_dbname;
400 String instance_type_dbname;
405 stmt.execute(String.format(createArtifactInstancesTableTemplate.toString(), instance_type_dbname, instance_type_dbname));
406 stmt.execute(String.format(instancesIdx1, instance_type_dbname, instance_type_dbname));
407 stmt.execute(String.format(instancesIdx2, instance_type_dbname, instance_type_dbname));
408 stmt.execute(String.format(instancesIdx3, instance_type_dbname, instance_type_dbname));
409 stmt.execute(String.format(instancesIdx4, instance_type_dbname, instance_type_dbname));
413 stmt.execute(String.format(createReferenceTypesTableTemplate.toString(), reference_type_dbname, reference_type_dbname));
414 stmt.execute(String.format(referenceTypesIdx1, reference_type_dbname, reference_type_dbname));
415 stmt.execute(String.format(referenceTypesIdx2, reference_type_dbname, reference_type_dbname));
418 }
catch (SQLException ex) {
419 LOGGER.log(Level.SEVERE,
"Error initializing db schema.", ex);
422 LOGGER.log(Level.SEVERE,
"Error getting default correlation types. Likely due to one or more Type's with an invalid db table name.");
438 &&
EamDbUtil.insertDefaultOrganization(conn);
443 boolean isChanged() {
448 return !dbName.equals(dbNameString)
449 || !dbDirectory.equals(dbDirectoryString)
450 || !Integer.toString(bulkThreshold).equals(bulkThresholdString);
466 if (dbName == null || dbName.isEmpty()) {
467 throw new EamDbException(
"Invalid database file name. Cannot be null or empty.");
468 }
else if (!Pattern.matches(DB_NAMES_REGEX, dbName)) {
469 throw new EamDbException(
"Invalid database file name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'.");
478 int getBulkThreshold() {
486 if (bulkThreshold > 0) {
489 throw new EamDbException(
"Invalid bulk threshold.");
510 if (dbDirectory != null && !dbDirectory.isEmpty()) {
513 throw new EamDbException(
"Invalid directory for sqlite database. Cannot empty");
536 String getValidationQuery() {
543 String getJDBCBaseURI() {
boolean createDbDirectory()
boolean insertDefaultDatabaseContent()
static boolean schemaVersionIsSet(Connection conn)
static final String PRAGMA_JOURNAL_WAL
static final String PRAGMA_SYNC_OFF
static boolean executeValidationQuery(Connection conn, String validationQuery)
static final int FILES_TYPE_ID
String getFileNameWithPath()
boolean verifyConnection()
boolean initializeDatabaseSchema()
Connection getEphemeralConnection()
final String VALIDATION_QUERY
static String correlationTypeToReferenceTableName(CorrelationAttribute.Type type)
final String DEFAULT_DBDIRECTORY
static final String PRAGMA_READ_UNCOMMITTED_TRUE
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 correlationTypeToInstanceTableName(CorrelationAttribute.Type type)
static String getConfigSetting(String moduleName, String settingName)
static final String PRAGMA_SYNC_NORMAL
final String DB_NAMES_REGEX
final String DEFAULT_DBNAME
void setDbName(String dbName)
synchronized static Logger getLogger(String name)
static List< CorrelationAttribute.Type > getDefaultCorrelationTypes()
static final String PRAGMA_FOREIGN_KEYS_ON
static final String PRAGMA_ENCODING_UTF8
void setDbDirectory(String dbDirectory)
boolean dbDirectoryExists()