Autopsy  4.20.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-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.autopsy.textextractors;
20 
21 import java.io.IOException;
22 import java.io.Reader;
23 import java.util.Iterator;
24 import java.util.Objects;
25 import java.util.function.Consumer;
26 import java.util.logging.Level;
30 import org.sleuthkit.datamodel.AbstractFile;
31 
41 final class SqliteTextExtractor implements TextExtractor {
42 
43  private static final String SQLITE_MIMETYPE = "application/x-sqlite3";
44  private static final Logger logger = Logger.getLogger(SqliteTextExtractor.class.getName());
45  private final AbstractFile file;
46 
47  SqliteTextExtractor(AbstractFile file) {
48  this.file = file;
49  }
58  @Override
59  public boolean isSupported() {
60  return SQLITE_MIMETYPE.equals(file.getMIMEType());
61  }
62 
72  @Override
73  public Reader getReader() throws InitReaderException {
74  return new SQLiteStreamReader(file);
75  }
76 
82  private class SQLiteStreamReader extends Reader {
83 
84  private final SQLiteTableReader reader;
85  private final AbstractFile file;
86 
87  private Iterator<String> tableNames;
88  private String currentTableName;
89 
90  private char[] buf;
92  private int totalColumns;
93 
94  private int bufIndex;
95 
104  SQLiteStreamReader(AbstractFile file) {
105  this.file = file;
106  reader = new SQLiteTableReader.Builder(file)
109  }
110 
122  private Consumer<Object> getForAllTableValuesStrategy() {
123  return new Consumer<Object>() {
124  private int columnIndex = 0;
125 
126  @Override
127  public void accept(Object value) {
128  columnIndex++;
129  //Ignore blobs
130  String objectStr = (value instanceof byte[]) ? "" : Objects.toString(value, "");
131 
132  if (columnIndex > 1 && columnIndex < totalColumns) {
133  objectStr += " ";
134  }
135  if (columnIndex == 1) {
136  objectStr = "\t" + objectStr + " ";
137  }
138  if (columnIndex == totalColumns) {
139  objectStr += "\n";
140  }
141 
142  fillBuffer(objectStr);
143  columnIndex %= totalColumns;
144  }
145  };
146  }
147 
160  private Consumer<String> getColumnNameStrategy() {
161  return new Consumer<String>() {
162  private int columnIndex = 0;
163 
164  @Override
165  public void accept(String columnName) {
166  if (columnIndex == 0) {
167  fillBuffer("\n" + currentTableName + "\n\n\t");
168  }
169  columnIndex++;
170 
171  fillBuffer(columnName + ((columnIndex == totalColumns) ? "\n" : " "));
172 
173  //Reset the columnCount to 0 for next table read
174  columnIndex %= totalColumns;
175  }
176  };
177  }
178 
186  private void fillBuffer(String val) {
187  for (int i = 0; i < val.length(); i++) {
188  if (bufIndex != buf.length) {
189  buf[bufIndex++] = val.charAt(i);
190  } else {
191  leftOvers = new ExcessBytes(val, i);
192  break;
193  }
194  }
195  }
196 
205  @Override
206  public int read(char[] cbuf, int off, int len) throws IOException {
207  buf = cbuf; //needs to be the same memory address and not a copy of the contents since we are filling it in
208 
209  bufIndex = off;
210 
211  //Lazily wait to get table names until first call to read.
212  if (Objects.isNull(tableNames)) {
213  try {
214  tableNames = reader.getTableNames().iterator();
215  } catch (SQLiteTableReaderException ex) {
216  //Can't get table names so can't read the file!
217  return -1;
218  }
219  }
220 
221  //If there are excess bytes from last read, then copy thoses in.
222  if (Objects.nonNull(leftOvers) && !leftOvers.isFinished()) {
223  bufIndex += leftOvers.read(cbuf, off, len);
224  }
225 
226  //Keep grabbing table names from the queue and reading them until
227  //our buffer is full.
228  while (bufIndex != len) {
229  if (Objects.isNull(currentTableName) || reader.isFinished()) {
230  if (tableNames.hasNext()) {
231  currentTableName = tableNames.next();
232  try {
233  totalColumns = reader.getColumnCount(currentTableName);
234  reader.read(currentTableName, () -> bufIndex == len);
235  } catch (SQLiteTableReaderException ex) {
236  logger.log(Level.WARNING, String.format(
237  "Error attempting to read file table: [%s]" //NON-NLS
238  + " for file: [%s] (id=%d).", currentTableName, //NON-NLS
239  file.getName(), file.getId()), ex.getMessage());
240  }
241  } else {
242  if (bufIndex == off) {
243  return -1;
244  }
245  return bufIndex;
246  }
247  } else {
248  try {
249  reader.read(currentTableName, () -> bufIndex == len);
250  } catch (SQLiteTableReaderException ex) {
251  logger.log(Level.WARNING, String.format(
252  "Error attempting to read file table: [%s]" //NON-NLS
253  + " for file: [%s] (id=%d).", currentTableName, //NON-NLS
254  file.getName(), file.getId()), ex.getMessage());
255  }
256  }
257  }
258 
259  return bufIndex;
260  }
261 
262  @Override
263  public void close() throws IOException {
264  try {
265  reader.close();
266  } catch (SQLiteTableReaderException ex) {
267  logger.log(Level.WARNING, "Could not close SQliteTableReader.", ex.getMessage());
268  }
269  }
270 
275  private class ExcessBytes {
276 
277  private final String entity;
278  private Integer pointer;
279 
280  ExcessBytes(String entity, Integer pointer) {
281  this.entity = entity;
282  this.pointer = pointer;
283  }
284 
285  boolean isFinished() {
286  return entity.length() == pointer;
287  }
288 
299  int read(char[] buf, int off, int len) {
300  for (int i = off; i < len; i++) {
301  if (isFinished()) {
302  return i - off;
303  }
304 
305  buf[i] = entity.charAt(pointer++);
306  }
307 
308  return len - off;
309  }
310  }
311  }
312 }

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.