Autopsy  4.8.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
SqliteTextExtractor.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2018-2018 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.autopsy.keywordsearch;
20 
21 import com.google.common.io.CharSource;
22 import java.io.IOException;
23 import java.io.Reader;
24 import java.sql.Connection;
25 import java.sql.DriverManager;
26 import java.sql.ResultSet;
27 import java.sql.ResultSetMetaData;
28 import java.sql.SQLException;
29 import java.sql.Statement;
30 import java.util.Collection;
31 import java.util.Iterator;
32 import java.util.LinkedList;
33 import java.util.logging.Level;
37 import org.sleuthkit.datamodel.Content;
38 import org.sleuthkit.datamodel.AbstractFile;
39 import org.sleuthkit.datamodel.TskCoreException;
40 
49 class SqliteTextExtractor extends ContentTextExtractor {
50 
51  private static final String SQLITE_MIMETYPE = "application/x-sqlite3";
52  private static final Logger logger = Logger.getLogger(SqliteTextExtractor.class.getName());
53  private static final CharSequence EMPTY_CHARACTER_SEQUENCE = "";
54 
55  @Override
56  boolean isContentTypeSpecific() {
57  return true;
58  }
59 
60  @Override
61  public boolean isDisabled() {
62  return false;
63  }
64 
65  @Override
66  public void logWarning(String msg, Exception exception) {
67  logger.log(Level.WARNING, msg, exception); //NON-NLS
68  }
69 
78  @Override
79  boolean isSupported(Content file, String detectedFormat) {
80  return SQLITE_MIMETYPE.equals(detectedFormat);
81  }
82 
93  @Override
94  public Reader getReader(Content source) throws TextExtractorException {
95  try {
96  //Firewall for any content that is not an AbstractFile
97  if (!AbstractFile.class.isInstance(source)) {
98  return CharSource.wrap(EMPTY_CHARACTER_SEQUENCE).openStream();
99  }
100  return new SQLiteTableReader((AbstractFile) source);
101  } catch (NoCurrentCaseException | IOException | TskCoreException
102  | ClassNotFoundException | SQLException ex) {
103  throw new TextExtractorException(
104  String.format("Encountered an issue while trying to initialize " //NON-NLS
105  + "a sqlite table steamer for abstract file with id: [%s], name: " //NON-NLS
106  + "[%s].", source.getId(), source.getName()), ex); //NON-NLS
107  }
108  }
109 
113  private class SQLiteTableReader extends Reader {
114 
115  private final Iterator<String> tableIterator;
116  private final Connection connection;
117  private Reader currentTableReader;
118  private final AbstractFile source;
119 
134  public SQLiteTableReader(AbstractFile file) throws NoCurrentCaseException,
135  IOException, TskCoreException, ClassNotFoundException, SQLException {
136  source = file;
137 
138  String localDiskPath = SqliteUtil.writeAbstractFileToLocalDisk(file);
140  Class.forName("org.sqlite.JDBC"); //NON-NLS
141  connection = DriverManager.getConnection("jdbc:sqlite:" + localDiskPath); //NON-NLS
142  tableIterator = getTables().iterator();
143  }
144 
150  private Collection<String> getTables() throws SQLException {
151  Collection<String> tableNames = new LinkedList<>();
152  try (Statement statement = connection.createStatement();
153  ResultSet resultSet = statement.executeQuery(
154  "SELECT name FROM sqlite_master "
155  + " WHERE type= 'table' ")) {
156  while (resultSet.next()) {
157  tableNames.add(resultSet.getString("name")); //NON-NLS
158  }
159  }
160  return tableNames;
161  }
162 
169  private String getTableAsString(String tableName) {
170  TableBuilder table = new TableBuilder();
171  table.addTableName(tableName);
172  String quotedTableName = "\"" + tableName + "\"";
173 
174  try (Statement statement = connection.createStatement();
175  ResultSet resultSet = statement.executeQuery(
176  "SELECT * FROM " + quotedTableName)) { //NON-NLS
177  ResultSetMetaData metaData = resultSet.getMetaData();
178  int columnCount = resultSet.getMetaData().getColumnCount();
179  Collection<String> row = new LinkedList<>();
180 
181  //Add column names once from metadata
182  for (int i = 1; i <= columnCount; i++) {
183  row.add(metaData.getColumnName(i));
184  }
185 
186  table.addHeader(row);
187  while (resultSet.next()) {
188  row = new LinkedList<>();
189  for (int i = 1; i <= columnCount; i++) {
190  Object result = resultSet.getObject(i);
191  String type = metaData.getColumnTypeName(i);
192  if (isValuableResult(result, type)) {
193  row.add(resultSet.getObject(i).toString());
194  }
195  }
196  table.addRow(row);
197  }
198  table.addCell("\n");
199  } catch (SQLException ex) {
200  logger.log(Level.WARNING, String.format(
201  "Error attempting to read file table: [%s]" //NON-NLS
202  + " for file: [%s] (id=%d).", tableName, //NON-NLS
203  source.getName(), source.getId()), ex);
204  }
205 
206  return table.toString();
207  }
208 
219  private boolean isValuableResult(Object result, String type) {
220  //Ignore nulls and blobs
221  return result != null && type.compareToIgnoreCase("blob") != 0;
222  }
223 
237  @Override
238  public int read(char[] cbuf, int off, int len) throws IOException {
239  if (currentTableReader == null) {
240  String tableResults = getNextTable();
241  if (tableResults == null) {
242  return -1;
243  }
244  currentTableReader = CharSource.wrap(tableResults).openStream();
245  }
246 
247  int charactersRead = currentTableReader.read(cbuf, off, len);
248  while (charactersRead == -1) {
249  String tableResults = getNextTable();
250  if (tableResults == null) {
251  return -1;
252  }
253  currentTableReader = CharSource.wrap(tableResults).openStream();
254  charactersRead = currentTableReader.read(cbuf, off, len);
255  }
256 
257  return charactersRead;
258  }
259 
268  private String getNextTable() {
269  if (tableIterator.hasNext()) {
270  return getTableAsString(tableIterator.next());
271  } else {
272  return null;
273  }
274  }
275 
282  @Override
283  public void close() throws IOException {
284  try {
285  connection.close();
286  } catch (SQLException ex) {
287  //Non-essential exception, user has no need for the connection
288  //object at this stage so closing details are not important
289  logger.log(Level.WARNING, "Could not close JDBC connection", ex);
290  }
291  }
292 
293  }
294 
299  private class TableBuilder {
300 
301  private final Integer DEFAULT_CAPACITY = 32000;
302  private final StringBuilder table = new StringBuilder(DEFAULT_CAPACITY);
303 
304  private static final String TAB = "\t";
305  private static final String NEW_LINE = "\n";
306  private static final String SPACE = " ";
307 
314  public void addTableName(String tableName) {
315  table.append(tableName)
316  .append(NEW_LINE)
317  .append(NEW_LINE);
318  }
319 
325  public void addHeader(Collection<String> vals) {
326  addRow(vals);
327  }
328 
334  public void addRow(Collection<String> vals) {
335  table.append(TAB);
336  vals.forEach((val) -> {
337  table.append(val);
338  table.append(SPACE);
339  });
340  table.append(NEW_LINE);
341  }
342 
343  public void addCell(String cell) {
344  table.append(cell);
345  }
346 
353  @Override
354  public String toString() {
355  return table.toString();
356  }
357  }
358 }
static void findAndCopySQLiteMetaFile(AbstractFile sqliteFile)
Definition: SqliteUtil.java:56
static String writeAbstractFileToLocalDisk(AbstractFile file)

Copyright © 2012-2018 Basis Technology. Generated on: Thu Oct 4 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.