Sleuth Kit Java Bindings (JNI)  4.10.0
Java bindings for using The Sleuth Kit
CaseDbAccessManager.java
Go to the documentation of this file.
1 /*
2  * Sleuth Kit Data Model
3  *
4  * Copyright 2018-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 java.sql.PreparedStatement;
22 import java.sql.ResultSet;
23 import java.sql.SQLException;
24 import java.sql.Statement;
25 import java.util.logging.Level;
26 import java.util.logging.Logger;
29 import static org.sleuthkit.datamodel.SleuthkitCase.closeStatement;
31 
37 public final class CaseDbAccessManager {
38 
42  public interface CaseDbAccessQueryCallback {
43 
52  void process(ResultSet resultSet);
53 
54  }
55 
56 
57  private static final Logger logger = Logger.getLogger(CaseDbAccessManager.class.getName());
58 
59  private final SleuthkitCase tskDB;
60 
68  this.tskDB = skCase;
69  }
70 
80  public boolean columnExists(String tableName, String columnName) throws TskCoreException {
81 
82  boolean doesColumnExists = false;
83  CaseDbTransaction localTrans = tskDB.beginTransaction();
84  try {
85  doesColumnExists = columnExists(tableName, columnName, localTrans);
86  localTrans.commit();
87  localTrans = null;
88  }
89  finally {
90  if (null != localTrans) {
91  try {
92  localTrans.rollback();
93  } catch (TskCoreException ex) {
94  logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex);
95  }
96  }
97  }
98 
99  return doesColumnExists;
100  }
101 
112  public boolean columnExists(String tableName, String columnName, CaseDbTransaction transaction) throws TskCoreException {
113 
114  boolean columnExists = false;
115  Statement statement = null;
116  ResultSet resultSet = null;
117  try {
118  CaseDbConnection connection = transaction.getConnection();
119  statement = connection.createStatement();
120  if (DbType.SQLITE == tskDB.getDatabaseType()) {
121  String tableInfoQuery = "PRAGMA table_info(%s)"; //NON-NLS
122  resultSet = statement.executeQuery(String.format(tableInfoQuery, tableName));
123  while (resultSet.next()) {
124  if (resultSet.getString("name").equalsIgnoreCase(columnName)) {
125  columnExists = true;
126  break;
127  }
128  }
129  }
130  else {
131  String tableInfoQueryTemplate = "SELECT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='%s' AND column_name='%s')"; //NON-NLS
132  resultSet = statement.executeQuery(String.format(tableInfoQueryTemplate, tableName.toLowerCase(), columnName.toLowerCase()));
133  if (resultSet.next()) {
134  columnExists = resultSet.getBoolean(1);
135  }
136  }
137  }
138  catch (SQLException ex) {
139  throw new TskCoreException("Error checking if column " + columnName + "exists ", ex);
140  }
141  finally {
142  if (resultSet != null) {
143  try {
144  resultSet.close();
145  } catch (SQLException ex2) {
146  logger.log(Level.WARNING, "Failed to to close resultset after checking column", ex2);
147  }
148  }
149  closeStatement(statement);
150  }
151  return columnExists;
152  }
153 
162  public boolean tableExists(String tableName) throws TskCoreException {
163 
164  boolean doesTableExist = false;
165  CaseDbTransaction localTrans = tskDB.beginTransaction();
166  try {
167  doesTableExist = tableExists(tableName, localTrans);
168  localTrans.commit();
169  localTrans = null;
170  }
171  finally {
172  if (null != localTrans) {
173  try {
174  localTrans.rollback();
175  } catch (TskCoreException ex) {
176  logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex); //NON-NLS
177  }
178  }
179  }
180 
181  return doesTableExist;
182  }
183 
193  public boolean tableExists(String tableName, CaseDbTransaction transaction) throws TskCoreException {
194 
195  boolean tableExists = false;
196  Statement statement = null;
197  ResultSet resultSet = null;
198  try {
199  CaseDbConnection connection = transaction.getConnection();
200  statement = connection.createStatement();
201  if (DbType.SQLITE == tskDB.getDatabaseType()) {
202  resultSet = statement.executeQuery("SELECT name FROM sqlite_master WHERE type='table'"); //NON-NLS
203  while (resultSet.next()) {
204  if (resultSet.getString("name").equalsIgnoreCase(tableName)) { //NON-NLS
205  tableExists = true;
206  break;
207  }
208  }
209  }
210  else {
211  String tableInfoQueryTemplate = "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name='%s')"; //NON-NLS
212  resultSet = statement.executeQuery(String.format(tableInfoQueryTemplate, tableName.toLowerCase()));
213  if (resultSet.next()) {
214  tableExists = resultSet.getBoolean(1);
215  }
216  }
217  }
218  catch (SQLException ex) {
219  throw new TskCoreException("Error checking if table " + tableName + "exists ", ex);
220  } finally {
221  if (resultSet != null) {
222  try {
223  resultSet.close();
224  } catch (SQLException ex2) {
225  logger.log(Level.WARNING, "Failed to to close resultset after checking table", ex2);
226  }
227  }
228  closeStatement(statement);
229  }
230  return tableExists;
231  }
232 
246  public void createTable(final String tableName, final String tableSchema) throws TskCoreException {
247 
248  validateTableName(tableName);
249  validateSQL(tableSchema);
250 
251  CaseDbConnection connection = tskDB.getConnection();
253 
254  Statement statement = null;
255  String createSQL = "CREATE TABLE IF NOT EXISTS " + tableName + " " + tableSchema;
256  try {
257  statement = connection.createStatement();
258  statement.execute(createSQL);
259  } catch (SQLException ex) {
260  throw new TskCoreException("Error creating table " + tableName, ex);
261  } finally {
262  closeStatement(statement);
263  connection.close();
265  }
266 
267  }
268 
277  public void alterTable(final String tableName, final String alterSQL) throws TskCoreException {
278 
279  CaseDbTransaction localTrans = tskDB.beginTransaction();
280  try {
281  alterTable(tableName, alterSQL, localTrans);
282  localTrans.commit();
283  localTrans = null;
284  } finally {
285  if (null != localTrans) {
286  try {
287  localTrans.rollback();
288  } catch (TskCoreException ex) {
289  logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex);
290  }
291  }
292  }
293  }
294 
304  public void alterTable(final String tableName, final String alterSQL, final CaseDbTransaction transaction) throws TskCoreException {
305 
306  validateTableName(tableName);
307  validateSQL(alterSQL);
308 
309  CaseDbConnection connection = transaction.getConnection();
310 
311  Statement statement = null;
312  String sql = "ALTER TABLE " + tableName + " " + alterSQL;
313 
314  try {
315  statement = connection.createStatement();
316  statement.execute(sql);
317  } catch (SQLException ex) {
318  // SQLite occasionally returns false for columnExists() if a table was just created with that column
319  // leading to "duplicate column name" exception.
320  // We ignore this exception
321  if (DbType.SQLITE == tskDB.getDatabaseType() &&
322  alterSQL.toLowerCase().contains("add column") &&
323  ex.getMessage().toLowerCase().contains("duplicate column name")) {
324  logger.log(Level.WARNING, String.format("Column being added by SQL = %s already exists in table %s", alterSQL, tableName));
325  return;
326  }
327  throw new TskCoreException(String.format("Error altering table %s with SQL = %s", tableName, sql), ex);
328  } finally {
329  closeStatement(statement);
330  }
331  }
332 
347  public void createIndex(final String indexName, final String tableName, final String colsSQL) throws TskCoreException {
348 
349  validateTableName(tableName);
350  validateIndexName(indexName);
351  validateSQL(colsSQL);
352 
353  CaseDbConnection connection = tskDB.getConnection();
355 
356  Statement statement = null;
357  String indexSQL = "CREATE INDEX IF NOT EXISTS " + indexName + " ON " + tableName + " " + colsSQL; // NON-NLS
358  try {
359  statement = connection.createStatement();
360  statement.execute(indexSQL);
361  } catch (SQLException ex) {
362  throw new TskCoreException("Error creating index " + tableName, ex);
363  } finally {
364  closeStatement(statement);
365  connection.close();
367  }
368  }
369 
380  public long insert(final String tableName, final String sql) throws TskCoreException {
381 
382  CaseDbTransaction localTrans = tskDB.beginTransaction();
383  try {
384  long rowId = insert(tableName, sql, localTrans);
385  localTrans.commit();
386  localTrans = null;
387  return rowId;
388  } finally {
389  if (null != localTrans) {
390  try {
391  localTrans.rollback();
392  } catch (TskCoreException ex) {
393  logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex);
394  }
395  }
396  }
397 
398  }
399 
416  public long insert(final String tableName, final String sql, final CaseDbTransaction transaction) throws TskCoreException {
417  long rowId = 0;
418 
419  validateTableName(tableName);
420  validateSQL(sql);
421 
422  CaseDbConnection connection = transaction.getConnection();
423 
424  PreparedStatement statement = null;
425  ResultSet resultSet;
426  String insertSQL = "INSERT";
427  if (DbType.SQLITE == tskDB.getDatabaseType()) {
428  insertSQL += " OR IGNORE";
429  }
430 
431  insertSQL = insertSQL+ " INTO " + tableName + " " + sql; // NON-NLS
432  try {
433  statement = connection.prepareStatement(insertSQL, Statement.RETURN_GENERATED_KEYS);
434  connection.executeUpdate(statement);
435 
436  resultSet = statement.getGeneratedKeys();
437  if (resultSet.next()) {
438  rowId = resultSet.getLong(1); //last_insert_rowid()
439  }
440  } catch (SQLException ex) {
441  throw new TskCoreException("Error inserting row in table " + tableName + " with sql = "+ insertSQL, ex);
442  } finally {
443  closeStatement(statement);
444  }
445 
446  return rowId;
447  }
448 
463  public long insertOrUpdate(final String tableName, final String sql) throws TskCoreException {
464 
465  CaseDbTransaction localTrans = tskDB.beginTransaction();
466  try {
467  long rowId = insertOrUpdate(tableName, sql, localTrans);
468  localTrans.commit();
469  localTrans = null;
470  return rowId;
471  } finally {
472  if (null != localTrans) {
473  try {
474  localTrans.rollback();
475  } catch (TskCoreException ex) {
476  logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex);
477  }
478  }
479  }
480 
481  }
482 
499  public long insertOrUpdate(final String tableName, final String sql, final CaseDbTransaction transaction) throws TskCoreException {
500  long rowId = 0;
501 
502  validateTableName(tableName);
503  validateSQL(sql);
504 
505  CaseDbConnection connection = transaction.getConnection();
506 
507  PreparedStatement statement = null;
508  ResultSet resultSet;
509  String insertSQL = "INSERT";
510  if (DbType.SQLITE == tskDB.getDatabaseType()) {
511  insertSQL += " OR REPLACE";
512  }
513 
514  insertSQL += " INTO " + tableName + " " + sql; // NON-NLS
515  try {
516  statement = connection.prepareStatement(insertSQL, Statement.RETURN_GENERATED_KEYS);
517  connection.executeUpdate(statement);
518 
519  resultSet = statement.getGeneratedKeys();
520  resultSet.next();
521  rowId = resultSet.getLong(1); //last_insert_rowid()
522  } catch (SQLException ex) {
523  throw new TskCoreException("Error inserting row in table " + tableName + " with sql = "+ insertSQL, ex);
524  } finally {
525  closeStatement(statement);
526  }
527 
528  return rowId;
529  }
530 
539  public void update(final String tableName, final String sql) throws TskCoreException {
540  CaseDbTransaction localTrans = tskDB.beginTransaction();
541  try {
542  update(tableName, sql, localTrans);
543  localTrans.commit();
544  localTrans = null;
545  } finally {
546  if (null != localTrans) {
547  try {
548  localTrans.rollback();
549  } catch (TskCoreException ex) {
550  logger.log(Level.SEVERE, "Failed to rollback transaction after exception", ex);
551  }
552  }
553  }
554  }
555 
556 
567  public void update(final String tableName, final String sql, CaseDbTransaction transaction ) throws TskCoreException {
568 
569  validateTableName(tableName);
570  validateSQL(sql);
571 
572  CaseDbConnection connection = transaction.getConnection();
573 
574  Statement statement = null;
575  String updateSQL = "UPDATE " + tableName + " " + sql; // NON-NLS
576 
577  try {
578  statement = connection.createStatement();
579  statement.executeUpdate(updateSQL);
580  } catch (SQLException ex) {
581  throw new TskCoreException("Error Updating table " + tableName, ex);
582  } finally {
583  closeStatement(statement);
584  }
585  }
586 
595  public void select(final String sql, final CaseDbAccessQueryCallback queryCallback) throws TskCoreException {
596 
597  if (queryCallback == null) {
598  throw new TskCoreException("Callback is null");
599  }
600 
601  validateSQL(sql);
602 
603  CaseDbConnection connection = tskDB.getConnection();
605 
606  Statement statement = null;
607  ResultSet resultSet;
608  String selectSQL = "SELECT " + sql; // NON-NLS
609  try {
610  statement = connection.createStatement();
611  resultSet = statement.executeQuery(selectSQL);
612  queryCallback.process(resultSet);
613  } catch (SQLException ex) {
614  throw new TskCoreException("Error running SELECT query.", ex);
615  } finally {
616  closeStatement(statement);
617  connection.close();
619  }
620  }
621 
630  public void delete(final String tableName, final String sql ) throws TskCoreException {
631  validateTableName(tableName);
632  validateSQL(sql);
633 
634  CaseDbConnection connection = tskDB.getConnection();
636 
637  Statement statement = null;
638  String deleteSQL = "DELETE FROM " + tableName + " " + sql; // NON-NLS
639  try {
640  statement = connection.createStatement();
641  statement.executeUpdate(deleteSQL);
642  } catch (SQLException ex) {
643  throw new TskCoreException("Error deleting row from table " + tableName, ex);
644  } finally {
645  closeStatement(statement);
646  connection.close();
648  }
649  }
650 
659  private void validateTableName(String tableName) throws TskCoreException {
660 
661  if (SleuthkitCase.getCoreTableNames().contains(tableName.toLowerCase())) {
662  throw new TskCoreException("Attempt to modify a core TSK table " + tableName);
663  }
664  if (tableName.toLowerCase().startsWith("tsk_")) {
665  throw new TskCoreException("Modifying tables with tsk_ prefix is not allowed. ");
666  }
667  }
668 
677  private void validateIndexName(String indexName) throws TskCoreException {
678 
679  if (indexName.isEmpty()) {
680  throw new TskCoreException("Invalid index name " + indexName);
681  }
682 
683  if (SleuthkitCase.getCoreIndexNames().contains(indexName.toLowerCase())) {
684  throw new TskCoreException("Attempt to modify a core TSK index " + indexName);
685  }
686  }
687 
695  private void validateSQL(String sql) throws TskCoreException {
696  /*
697  * TODO (JIRA-5950): Need SQL injection defense in CaseDbAccessManager
698  */
699  }
700 
701 }
boolean tableExists(String tableName, CaseDbTransaction transaction)
long insertOrUpdate(final String tableName, final String sql)
void update(final String tableName, final String sql)
long insert(final String tableName, final String sql, final CaseDbTransaction transaction)
void alterTable(final String tableName, final String alterSQL, final CaseDbTransaction transaction)
void createIndex(final String indexName, final String tableName, final String colsSQL)
boolean columnExists(String tableName, String columnName)
void createTable(final String tableName, final String tableSchema)
long insertOrUpdate(final String tableName, final String sql, final CaseDbTransaction transaction)
void alterTable(final String tableName, final String alterSQL)
void select(final String sql, final CaseDbAccessQueryCallback queryCallback)
void update(final String tableName, final String sql, CaseDbTransaction transaction)
long insert(final String tableName, final String sql)
boolean columnExists(String tableName, String columnName, CaseDbTransaction transaction)

Copyright © 2011-2020 Brian Carrier. (carrier -at- sleuthkit -dot- org)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.