mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-09 06:39:33 +00:00
Merge branch '4388-implement-sqlite-streaming-design' into 4389-sqlite-stream-impl
This commit is contained in:
commit
11943c3923
@ -16,7 +16,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sleuthkit.autopsy.coreutils;
|
package org.sleuthkit.autopsy.coreutils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -42,19 +41,41 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
|||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Reads row by row through SQLite tables and performs user-defined actions on the row values.
|
||||||
* @author dsmyda
|
* Table values are processed by data type. Users configure these actions for certain data types
|
||||||
|
* in the Builder. Example usage:
|
||||||
|
*
|
||||||
|
* SQLiteTableReader reader = new SQLiteTableReader.Builder(file)
|
||||||
|
* .onInteger((i) -> {
|
||||||
|
* System.out.println(i);
|
||||||
|
* }).build();
|
||||||
|
* reader.read(tableName);
|
||||||
|
*
|
||||||
|
* or
|
||||||
|
*
|
||||||
|
* SQLiteTableReader reader = new SQLiteTableReader.Builder(file)
|
||||||
|
* .onInteger(new Consumer<Integer>() {
|
||||||
|
* @Override
|
||||||
|
* public void accept(Integer i) {
|
||||||
|
* System.out.println(i);
|
||||||
|
* }
|
||||||
|
* }).build();
|
||||||
|
* reader.reader(tableName);
|
||||||
|
*
|
||||||
|
* Invocation of read(String tableName) causes that table name to be processed row by row.
|
||||||
|
* When an Integer is encountered, its value will be passed to the Consumer that
|
||||||
|
* was defined above.
|
||||||
*/
|
*/
|
||||||
public class SQLiteTableReader implements AutoCloseable {
|
public class SQLiteTableReader implements AutoCloseable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Builder patten for configuring SQLiteTableReader instances.
|
||||||
*/
|
*/
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
|
||||||
private final AbstractFile file;
|
private final AbstractFile file;
|
||||||
private Consumer<String> onColumnNameAction;
|
private Consumer<String> onColumnNameAction;
|
||||||
|
|
||||||
private Consumer<String> onStringAction;
|
private Consumer<String> onStringAction;
|
||||||
private Consumer<Long> onLongAction;
|
private Consumer<Long> onLongAction;
|
||||||
private Consumer<Integer> onIntegerAction;
|
private Consumer<Integer> onIntegerAction;
|
||||||
@ -63,7 +84,7 @@ public class SQLiteTableReader implements AutoCloseable {
|
|||||||
private Consumer<Object> forAllAction;
|
private Consumer<Object> forAllAction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a SQLiteTableReaderBuilder for this abstract file.
|
* Creates a Builder for this abstract file.
|
||||||
*
|
*
|
||||||
* @param file
|
* @param file
|
||||||
*/
|
*/
|
||||||
@ -72,12 +93,12 @@ public class SQLiteTableReader implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify a function to handle MetaData parsing. The MetaData object
|
* Specify a function to do on column names. Column names will be read
|
||||||
* will be parsed before any contents are read from the table.
|
* from left to right.
|
||||||
*
|
*
|
||||||
* @param action
|
* @param action Consumer of column name strings
|
||||||
*
|
*
|
||||||
* @return
|
* @return Builder reference
|
||||||
*/
|
*/
|
||||||
public Builder onColumnNames(Consumer<String> action) {
|
public Builder onColumnNames(Consumer<String> action) {
|
||||||
this.onColumnNameAction = action;
|
this.onColumnNameAction = action;
|
||||||
@ -85,12 +106,12 @@ public class SQLiteTableReader implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify a function to do on receiving a database entry that is type
|
* Specify a function to do when encountering a database value that is
|
||||||
* String.
|
* of java type String.
|
||||||
*
|
*
|
||||||
* @param action
|
* @param action Consumer of strings
|
||||||
*
|
*
|
||||||
* @return
|
* @return Builder reference
|
||||||
*/
|
*/
|
||||||
public Builder onString(Consumer<String> action) {
|
public Builder onString(Consumer<String> action) {
|
||||||
this.onStringAction = action;
|
this.onStringAction = action;
|
||||||
@ -98,12 +119,12 @@ public class SQLiteTableReader implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify a function to do on receiving a database entry that is type
|
* Specify a function to do when encountering a database value that is
|
||||||
* Integer.
|
* of java type Integer.
|
||||||
*
|
*
|
||||||
* @param action
|
* @param action Consumer of integer
|
||||||
*
|
*
|
||||||
* @return
|
* @return Builder reference
|
||||||
*/
|
*/
|
||||||
public Builder onInteger(Consumer<Integer> action) {
|
public Builder onInteger(Consumer<Integer> action) {
|
||||||
this.onIntegerAction = action;
|
this.onIntegerAction = action;
|
||||||
@ -111,32 +132,38 @@ public class SQLiteTableReader implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify a function to do on receiving a database entry that is type
|
* Specify a function to do when encountering a database value that is
|
||||||
* Real.
|
* of java type Double.
|
||||||
*
|
*
|
||||||
* @param action
|
* @param action Consumer of doubles
|
||||||
*
|
*
|
||||||
* @return
|
* @return Builder reference
|
||||||
*/
|
*/
|
||||||
public Builder onFloat(Consumer<Double> action) {
|
public Builder onFloat(Consumer<Double> action) {
|
||||||
this.onFloatAction = action;
|
this.onFloatAction = action;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Specify a function to do when encountering a database value that is
|
||||||
* @param action
|
* of java type Long.
|
||||||
* @return
|
*
|
||||||
|
* @param action Consumer of longs
|
||||||
|
*
|
||||||
|
* @return Builder reference
|
||||||
*/
|
*/
|
||||||
public Builder onLong(Consumer<Long> action) {
|
public Builder onLong(Consumer<Long> action) {
|
||||||
this.onLongAction = action;
|
this.onLongAction = action;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Specify a function to do when encountering a database value that is
|
||||||
* @param action
|
* of java type byte[] aka blob.
|
||||||
* @return
|
*
|
||||||
|
* @param action Consumer of blobs
|
||||||
|
*
|
||||||
|
* @return Builder reference
|
||||||
*/
|
*/
|
||||||
public Builder onBlob(Consumer<byte[]> action) {
|
public Builder onBlob(Consumer<byte[]> action) {
|
||||||
this.onBlobAction = action;
|
this.onBlobAction = action;
|
||||||
@ -144,11 +171,13 @@ public class SQLiteTableReader implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify a function to do for any database entry, regardless of type.
|
* Specify a function to do when encountering any database value,
|
||||||
|
* regardless of type. This function only captures database values, not
|
||||||
|
* column names.
|
||||||
*
|
*
|
||||||
* @param action
|
* @param action Consumer of objects
|
||||||
*
|
*
|
||||||
* @return
|
* @return Builder reference
|
||||||
*/
|
*/
|
||||||
public Builder forAll(Consumer<Object> action) {
|
public Builder forAll(Consumer<Object> action) {
|
||||||
this.forAllAction = action;
|
this.forAllAction = action;
|
||||||
@ -156,10 +185,10 @@ public class SQLiteTableReader implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pass all params to the SQLTableStream so that it can iterate through
|
* Creates a SQLiteTableReader instance given this Builder
|
||||||
* the table
|
* configuration.
|
||||||
*
|
*
|
||||||
* @return
|
* @return SQLiteTableReader instance
|
||||||
*/
|
*/
|
||||||
public SQLiteTableReader build() {
|
public SQLiteTableReader build() {
|
||||||
return new SQLiteTableReader(this);
|
return new SQLiteTableReader(this);
|
||||||
@ -182,20 +211,15 @@ public class SQLiteTableReader implements AutoCloseable {
|
|||||||
|
|
||||||
//Iteration state variables
|
//Iteration state variables
|
||||||
private Integer currRowColumnIndex;
|
private Integer currRowColumnIndex;
|
||||||
private boolean unfinishedRowState;
|
|
||||||
private Integer columnNameIndex;
|
private Integer columnNameIndex;
|
||||||
private Integer currentColumnCount;
|
private Integer currentColumnCount;
|
||||||
private ResultSetMetaData currentMetadata;
|
private ResultSetMetaData currentMetadata;
|
||||||
|
|
||||||
private boolean isFinished;
|
private boolean liveResultSet;
|
||||||
private boolean hasOpened;
|
|
||||||
private String prevTableName;
|
private String prevTableName;
|
||||||
|
|
||||||
private final BooleanSupplier alwaysFalseCondition = () -> {return false;};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a new table stream given the parameters passed in from the
|
* Assigns references to each action based on the Builder configuration.
|
||||||
* StreamBuilder above.
|
|
||||||
*/
|
*/
|
||||||
private SQLiteTableReader(Builder builder) {
|
private SQLiteTableReader(Builder builder) {
|
||||||
|
|
||||||
@ -209,66 +233,80 @@ public class SQLiteTableReader implements AutoCloseable {
|
|||||||
|
|
||||||
this.file = builder.file;
|
this.file = builder.file;
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> Consumer<T> nonNullValue(Consumer<T> action) {
|
|
||||||
if (Objects.nonNull(action)) {
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
|
|
||||||
//No-op lambda, keep from NPE or having to check during iteration
|
/**
|
||||||
//if action == null.
|
* Ensures the action is null safe. If action is left null, then during
|
||||||
return (NO_OP) -> {};
|
* iteration null checks would be necessary. To mitigate against that, no-op
|
||||||
|
* lambdas are substituted for null values.
|
||||||
|
*
|
||||||
|
* @param <T> Generic type of consumer
|
||||||
|
* @param action Consumer for generic type, supplied by Builder.
|
||||||
|
*
|
||||||
|
* @return If action is null, then a no-op lambda, if not then the action
|
||||||
|
* itself.
|
||||||
|
*/
|
||||||
|
private <T> Consumer<T> nonNullValue(Consumer<T> action) {
|
||||||
|
return (Objects.nonNull(action)) ? action : NO_OP -> {
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get table names from database
|
* Fetches all table names from the database.
|
||||||
*
|
*
|
||||||
* @return
|
* @return List of all table names found while querying the sqlite_master
|
||||||
* @throws org.sleuthkit.autopsy.coreutils.SQLiteTableReaderException
|
* table
|
||||||
*
|
*
|
||||||
|
* @throws SQLiteTableReaderException
|
||||||
*/
|
*/
|
||||||
public List<String> getTableNames() throws SQLiteTableReaderException {
|
public List<String> getTableNames() throws SQLiteTableReaderException {
|
||||||
ensureOpen();
|
ensureOpen();
|
||||||
|
|
||||||
List<String> tableNames = new ArrayList<>();
|
|
||||||
|
|
||||||
try (ResultSet tableNameResult = conn.createStatement()
|
try (ResultSet tableNameResult = conn.createStatement()
|
||||||
.executeQuery("SELECT name FROM sqlite_master "
|
.executeQuery("SELECT name FROM sqlite_master "
|
||||||
+ " WHERE type= 'table' ")) {
|
+ " WHERE type= 'table' ")) {
|
||||||
|
List<String> tableNames = new ArrayList<>();
|
||||||
while (tableNameResult.next()) {
|
while (tableNameResult.next()) {
|
||||||
tableNames.add(tableNameResult.getString("name")); //NON-NLS
|
tableNames.add(tableNameResult.getString("name")); //NON-NLS
|
||||||
}
|
}
|
||||||
|
return tableNames;
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new SQLiteTableReaderException(ex);
|
throw new SQLiteTableReaderException(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tableNames;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Fetches the row count.
|
||||||
* @param tableName
|
*
|
||||||
* @return
|
* @param tableName Source table to count
|
||||||
* @throws org.sleuthkit.autopsy.coreutils.SQLiteTableReaderException
|
*
|
||||||
|
* @return Count as an integer
|
||||||
|
*
|
||||||
|
* @throws SQLiteTableReaderException
|
||||||
*/
|
*/
|
||||||
public int getRowCount(String tableName) throws SQLiteTableReaderException {
|
public int getRowCount(String tableName) throws SQLiteTableReaderException {
|
||||||
ensureOpen();
|
ensureOpen();
|
||||||
|
|
||||||
try (ResultSet countResult = conn.createStatement()
|
try (ResultSet countResult = conn.createStatement()
|
||||||
.executeQuery("SELECT count (*) as count FROM " +
|
.executeQuery("SELECT count (*) as count FROM "
|
||||||
"\"" + tableName + "\"")) {
|
+ "\"" + tableName + "\"")) {
|
||||||
return countResult.getInt("count");
|
return countResult.getInt("count");
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new SQLiteTableReaderException(ex);
|
throw new SQLiteTableReaderException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the column count of the table.
|
||||||
|
*
|
||||||
|
* @param tableName Source table to count
|
||||||
|
*
|
||||||
|
* @return Count as an integer
|
||||||
|
*
|
||||||
|
* @throws SQLiteTableReaderException
|
||||||
|
*/
|
||||||
public int getColumnCount(String tableName) throws SQLiteTableReaderException {
|
public int getColumnCount(String tableName) throws SQLiteTableReaderException {
|
||||||
ensureOpen();
|
ensureOpen();
|
||||||
|
|
||||||
try (ResultSet columnCount = conn.createStatement()
|
try (ResultSet columnCount = conn.createStatement()
|
||||||
.executeQuery("SELECT * FROM " +
|
.executeQuery("SELECT * FROM "
|
||||||
"\"" + tableName + "\"")) {
|
+ "\"" + tableName + "\"")) {
|
||||||
return columnCount.getMetaData().getColumnCount();
|
return columnCount.getMetaData().getColumnCount();
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new SQLiteTableReaderException(ex);
|
throw new SQLiteTableReaderException(ex);
|
||||||
@ -276,151 +314,135 @@ public class SQLiteTableReader implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Reads column names and values from the table. Only actions that were
|
||||||
* @param tableName
|
* configured in the Builder will be invoked during iteration. Iteration
|
||||||
* @throws org.sleuthkit.autopsy.coreutils.SQLiteTableReaderException
|
* will stop when the table read has completed or an exception was
|
||||||
|
* encountered.
|
||||||
|
*
|
||||||
|
* @param tableName Source table to read
|
||||||
|
*
|
||||||
|
* @throws SQLiteTableReaderException
|
||||||
*/
|
*/
|
||||||
public void read(String tableName) throws SQLiteTableReaderException {
|
public void read(String tableName) throws SQLiteTableReaderException {
|
||||||
readHelper("SELECT * FROM \"" + tableName +"\"", alwaysFalseCondition);
|
readHelper("SELECT * FROM \"" + tableName + "\"", () -> false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read x number of rows (limit), starting from row number y (offset) in
|
* Reads column names and values from the table. Only actions that were
|
||||||
* table z (tableName).
|
* configured in the Builder will be invoked during iteration. Column names
|
||||||
|
* are only read during the first call to this function. Iteration will stop
|
||||||
|
* when the table read has completed or an exception was encountered.
|
||||||
*
|
*
|
||||||
* @param tableName
|
* @param tableName Source table to perform a read
|
||||||
* @param limit
|
* @param limit Number of rows to read from the table
|
||||||
* @param offset
|
* @param offset Starting row to read from in the table
|
||||||
* @throws org.sleuthkit.autopsy.coreutils.SQLiteTableReaderException
|
*
|
||||||
|
* @throws SQLiteTableReaderException
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void read(String tableName, int limit, int offset) throws SQLiteTableReaderException {
|
public void read(String tableName, int limit, int offset) throws SQLiteTableReaderException {
|
||||||
readHelper("SELECT * FROM \"" + tableName +"\" LIMIT " + limit
|
readHelper("SELECT * FROM \"" + tableName + "\" LIMIT " + limit
|
||||||
+ " OFFSET " + offset, alwaysFalseCondition);
|
+ " OFFSET " + offset, () -> false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterate through the table stopping if we are done, an exception is
|
* Reads column names and values from the table. Iteration will stop when
|
||||||
* thrown, or the condition is false!
|
* the condition is true.
|
||||||
*
|
*
|
||||||
* @param tableName
|
* @param tableName Source table to perform a read
|
||||||
* @param condition
|
* @param condition Condition to stop iteration when true
|
||||||
* @throws org.sleuthkit.autopsy.coreutils.SQLiteTableReaderException
|
*
|
||||||
|
* @throws SQLiteTableReaderException
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void read(String tableName, BooleanSupplier condition) throws SQLiteTableReaderException {
|
public void read(String tableName, BooleanSupplier condition) throws SQLiteTableReaderException {
|
||||||
if(Objects.nonNull(prevTableName) && prevTableName.equals(tableName)) {
|
if (Objects.nonNull(prevTableName) && prevTableName.equals(tableName)) {
|
||||||
readHelper("SELECT * FROM \"" + tableName + "\"", condition);
|
readHelper("SELECT * FROM \"" + tableName + "\"", condition);
|
||||||
} else {
|
} else {
|
||||||
prevTableName = tableName;
|
prevTableName = tableName;
|
||||||
closeResultSet();
|
closeTableResources();
|
||||||
readHelper("SELECT * FROM \"" + tableName + "\"", condition);
|
readHelper("SELECT * FROM \"" + tableName + "\"", condition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterate through the entire table calling the correct function given the
|
* Performs the result set iteration and is responsible for maintaining state
|
||||||
* datatype. Only stop when there is nothing left to read or a SQLException
|
* of the read over multiple invocations.
|
||||||
* is thrown.
|
|
||||||
*
|
*
|
||||||
* @param tableName
|
* @throws SQLiteTableReaderException
|
||||||
*
|
|
||||||
* @throws org.sleuthkit.autopsy.core.AutopsySQLiteException
|
|
||||||
*/
|
*/
|
||||||
private void readHelper(String query, BooleanSupplier condition) throws SQLiteTableReaderException {
|
private void readHelper(String query, BooleanSupplier condition) throws SQLiteTableReaderException {
|
||||||
try {
|
try {
|
||||||
if(!hasOpened) {
|
if (!liveResultSet) {
|
||||||
openResultSet(query);
|
openTableResources(query);
|
||||||
currentMetadata = queryResults.getMetaData();
|
|
||||||
currentColumnCount = currentMetadata.getColumnCount();
|
|
||||||
columnNameIndex = 1;
|
columnNameIndex = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
isFinished = false;
|
//Process column names before reading the database table values
|
||||||
|
for (; columnNameIndex <= currentColumnCount; columnNameIndex++) {
|
||||||
for(; columnNameIndex <= currentColumnCount; columnNameIndex++) {
|
if (condition.getAsBoolean()) {
|
||||||
this.onColumnNameAction.accept(currentMetadata.getColumnName(columnNameIndex));
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
while (unfinishedRowState || queryResults.next()) {
|
|
||||||
if (!unfinishedRowState) {
|
|
||||||
currRowColumnIndex = 1;
|
|
||||||
}
|
}
|
||||||
|
this.onColumnNameAction.accept(currentMetadata
|
||||||
for (; currRowColumnIndex <= currentColumnCount; currRowColumnIndex++) {
|
.getColumnName(columnNameIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
//currRowColumnIndex > 0 means we are still reading the current result set row
|
||||||
|
while (currRowColumnIndex > 0 || queryResults.next()) {
|
||||||
|
while(currRowColumnIndex < currentColumnCount) {
|
||||||
if (condition.getAsBoolean()) {
|
if (condition.getAsBoolean()) {
|
||||||
unfinishedRowState = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//getObject automatically instiantiates the correct java data type
|
Object item = queryResults.getObject(++currRowColumnIndex);
|
||||||
Object item = queryResults.getObject(currRowColumnIndex);
|
if (item instanceof String) {
|
||||||
if(item instanceof String) {
|
|
||||||
this.onStringAction.accept((String) item);
|
this.onStringAction.accept((String) item);
|
||||||
} else if(item instanceof Integer) {
|
} else if (item instanceof Integer) {
|
||||||
this.onIntegerAction.accept((Integer) item);
|
this.onIntegerAction.accept((Integer) item);
|
||||||
} else if(item instanceof Double) {
|
} else if (item instanceof Double) {
|
||||||
this.onFloatAction.accept((Double) item);
|
this.onFloatAction.accept((Double) item);
|
||||||
} else if(item instanceof Long) {
|
} else if (item instanceof Long) {
|
||||||
this.onLongAction.accept((Long) item);
|
this.onLongAction.accept((Long) item);
|
||||||
} else if(item instanceof byte[]) {
|
} else if (item instanceof byte[]) {
|
||||||
this.onBlobAction.accept((byte[]) item);
|
this.onBlobAction.accept((byte[]) item);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.forAllAction.accept(item);
|
this.forAllAction.accept(item);
|
||||||
}
|
}
|
||||||
|
//Wrap column index back around if we've reached the end of the row
|
||||||
unfinishedRowState = false;
|
currRowColumnIndex = (currRowColumnIndex % currentColumnCount);
|
||||||
}
|
}
|
||||||
|
closeTableResources();
|
||||||
isFinished = true;
|
|
||||||
closeResultSet();
|
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
closeResultSet();
|
closeTableResources();
|
||||||
isFinished = true;
|
|
||||||
throw new SQLiteTableReaderException(ex);
|
throw new SQLiteTableReaderException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Ensures that the underlying database connection is open. This entails
|
||||||
* @throws org.sleuthkit.autopsy.core.AutopsySQLiteException
|
* copying the abstract file contents to temp directory, copying over any
|
||||||
|
* WAL or SHM files and getting the connection from the DriverManager.
|
||||||
|
*
|
||||||
|
* @throws SQLiteTableReaderException
|
||||||
*/
|
*/
|
||||||
private void ensureOpen() throws SQLiteTableReaderException {
|
private void ensureOpen() throws SQLiteTableReaderException {
|
||||||
if (Objects.isNull(conn)) {
|
if (Objects.isNull(conn)) {
|
||||||
try {
|
try {
|
||||||
Class.forName("org.sqlite.JDBC"); //NON-NLS
|
Class.forName("org.sqlite.JDBC"); //NON-NLS
|
||||||
String localDiskPath = writeAbstractFileToLocalDisk(file, file.getId());
|
String localDiskPath = copyFileToTempDirectory(file, file.getId());
|
||||||
findAndCopySQLiteMetaFile(file);
|
|
||||||
|
//Find and copy both WAL and SHM meta files
|
||||||
|
findAndCopySQLiteMetaFile(file, file.getName() + "-wal");
|
||||||
|
findAndCopySQLiteMetaFile(file, file.getName() + "-shm");
|
||||||
conn = DriverManager.getConnection("jdbc:sqlite:" + localDiskPath);
|
conn = DriverManager.getConnection("jdbc:sqlite:" + localDiskPath);
|
||||||
} catch (NoCurrentCaseException | TskCoreException | IOException |
|
} catch (NoCurrentCaseException | TskCoreException | IOException
|
||||||
ClassNotFoundException | SQLException ex) {
|
| ClassNotFoundException | SQLException ex) {
|
||||||
throw new SQLiteTableReaderException(ex);
|
throw new SQLiteTableReaderException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Overloaded implementation of
|
|
||||||
* {@link #findAndCopySQLiteMetaFile(AbstractFile, String) findAndCopySQLiteMetaFile}
|
|
||||||
* , automatically tries to copy -wal and -shm files without needing to know
|
|
||||||
* their existence.
|
|
||||||
*
|
|
||||||
* @param sqliteFile file which has -wal and -shm meta files
|
|
||||||
*
|
|
||||||
* @throws NoCurrentCaseException Case has been closed.
|
|
||||||
* @throws TskCoreException fileManager cannot find AbstractFile
|
|
||||||
* files.
|
|
||||||
* @throws IOException Issue during writing to file.
|
|
||||||
*/
|
|
||||||
private void findAndCopySQLiteMetaFile(AbstractFile sqliteFile)
|
|
||||||
throws NoCurrentCaseException, TskCoreException, IOException {
|
|
||||||
|
|
||||||
findAndCopySQLiteMetaFile(sqliteFile, sqliteFile.getName() + "-wal");
|
|
||||||
findAndCopySQLiteMetaFile(sqliteFile, sqliteFile.getName() + "-shm");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches for a meta file associated with the give SQLite database. If
|
* Searches for a meta file associated with the give SQLite database. If
|
||||||
* found, it copies this file into the temp directory of the current case.
|
* found, it copies this file into the temp directory of the current case.
|
||||||
@ -447,11 +469,11 @@ public class SQLiteTableReader implements AutoCloseable {
|
|||||||
|
|
||||||
if (metaFiles != null) {
|
if (metaFiles != null) {
|
||||||
for (AbstractFile metaFile : metaFiles) {
|
for (AbstractFile metaFile : metaFiles) {
|
||||||
writeAbstractFileToLocalDisk(metaFile, sqliteFile.getId());
|
copyFileToTempDirectory(metaFile, sqliteFile.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies the file contents into a unique path in the current case temp
|
* Copies the file contents into a unique path in the current case temp
|
||||||
* directory.
|
* directory.
|
||||||
@ -463,7 +485,7 @@ public class SQLiteTableReader implements AutoCloseable {
|
|||||||
* @throws IOException Exception writing file contents
|
* @throws IOException Exception writing file contents
|
||||||
* @throws NoCurrentCaseException Current case closed during file copying
|
* @throws NoCurrentCaseException Current case closed during file copying
|
||||||
*/
|
*/
|
||||||
private String writeAbstractFileToLocalDisk(AbstractFile file, long id)
|
private String copyFileToTempDirectory(AbstractFile file, long id)
|
||||||
throws IOException, NoCurrentCaseException {
|
throws IOException, NoCurrentCaseException {
|
||||||
|
|
||||||
String localDiskPath = Case.getCurrentCaseThrows().getTempDirectory()
|
String localDiskPath = Case.getCurrentCaseThrows().getTempDirectory()
|
||||||
@ -474,74 +496,82 @@ public class SQLiteTableReader implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
return localDiskPath;
|
return localDiskPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Executes the query and assigns resource references to instance variables.
|
||||||
* @param query
|
*
|
||||||
* @throws SQLException
|
* @param query Input query to execute
|
||||||
|
*
|
||||||
|
* @throws SQLiteTableReaderException
|
||||||
*/
|
*/
|
||||||
private void openResultSet(String query) throws SQLiteTableReaderException {
|
private void openTableResources(String query) throws SQLiteTableReaderException {
|
||||||
ensureOpen();
|
try {
|
||||||
|
ensureOpen();
|
||||||
try {
|
|
||||||
statement = conn.prepareStatement(query);
|
statement = conn.prepareStatement(query);
|
||||||
|
|
||||||
queryResults = statement.executeQuery();
|
queryResults = statement.executeQuery();
|
||||||
hasOpened = true;
|
currentMetadata = queryResults.getMetaData();
|
||||||
|
currentColumnCount = currentMetadata.getColumnCount();
|
||||||
|
liveResultSet = true;
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new SQLiteTableReaderException(ex);
|
throw new SQLiteTableReaderException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Ensures both the statement and the result set for a table are closed.
|
||||||
*/
|
*/
|
||||||
private void closeResultSet() {
|
private void closeTableResources() {
|
||||||
try {
|
try {
|
||||||
if(Objects.nonNull(statement)) {
|
if (Objects.nonNull(statement)) {
|
||||||
statement.close();
|
statement.close();
|
||||||
}
|
}
|
||||||
if(Objects.nonNull(queryResults)) {
|
if (Objects.nonNull(queryResults)) {
|
||||||
queryResults.close();
|
queryResults.close();
|
||||||
}
|
}
|
||||||
hasOpened = false;
|
liveResultSet = false;
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
//Do nothing, can't close.. tried our best.
|
//Do nothing, can't close.. tried our best.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes all connections with the database.
|
* Closes all resources attached to the database file.
|
||||||
|
*
|
||||||
|
* @throws SQLiteTableReaderException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() throws SQLiteTableReaderException {
|
||||||
try {
|
try {
|
||||||
closeResultSet();
|
closeTableResources();
|
||||||
if(Objects.nonNull(conn)) {
|
if (Objects.nonNull(conn)) {
|
||||||
conn.close();
|
conn.close();
|
||||||
}
|
}
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
//Do nothing, can't close.. tried our best.
|
throw new SQLiteTableReaderException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if there is still work to do on the result set.
|
* Provides status of the current read operation.
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean isFinished() {
|
public boolean isFinished() {
|
||||||
return isFinished;
|
return !liveResultSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last ditch effort to close the connections.
|
* Last ditch effort to close the connections during garbage collection.
|
||||||
*
|
*
|
||||||
* @throws Throwable
|
* @throws Throwable
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void finalize() throws Throwable {
|
public void finalize() throws Throwable {
|
||||||
super.finalize();
|
super.finalize();
|
||||||
close();
|
try {
|
||||||
|
close();
|
||||||
|
} catch (SQLiteTableReaderException ex) {
|
||||||
|
//Do nothing, we tried out best to close the connection.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,43 @@
|
|||||||
/*
|
/*
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
* Autopsy Forensic Browser
|
||||||
* To change this template file, choose Tools | Templates
|
*
|
||||||
* and open the template in the editor.
|
* Copyright 2018-2018 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.coreutils;
|
package org.sleuthkit.autopsy.coreutils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Provides a system exception for the SQLiteTableReader class.
|
||||||
* @author dsmyda
|
|
||||||
*/
|
*/
|
||||||
public class SQLiteTableReaderException extends Exception {
|
public class SQLiteTableReaderException extends Exception {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts both a message and a parent exception.
|
||||||
|
*
|
||||||
|
* @param msg Message detailing the cause
|
||||||
|
* @param ex Parent exception
|
||||||
|
*/
|
||||||
public SQLiteTableReaderException(String msg, Throwable ex) {
|
public SQLiteTableReaderException(String msg, Throwable ex) {
|
||||||
super(msg, ex);
|
super(msg, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts only a parent exception.
|
||||||
|
*
|
||||||
|
* @param ex Parent exception
|
||||||
|
*/
|
||||||
public SQLiteTableReaderException(Throwable ex) {
|
public SQLiteTableReaderException(Throwable ex) {
|
||||||
super(ex);
|
super(ex);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user