Autopsy  4.5.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
SQLiteViewer.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 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.contentviewers;
20 
21 import java.awt.BorderLayout;
22 import java.awt.Component;
23 import java.io.File;
24 import java.io.IOException;
25 import java.sql.Connection;
26 import java.sql.DriverManager;
27 import java.sql.ResultSet;
28 import java.sql.ResultSetMetaData;
29 import java.sql.SQLException;
30 import java.sql.Statement;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.LinkedHashMap;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Objects;
38 import java.util.TreeMap;
39 import java.util.concurrent.ExecutionException;
40 import java.util.logging.Level;
41 import javax.swing.JComboBox;
42 import javax.swing.SwingWorker;
43 import org.openide.util.NbBundle;
47 import org.sleuthkit.datamodel.AbstractFile;
48 
49 public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
50 
51  public static final String[] SUPPORTED_MIMETYPES = new String[]{"application/x-sqlite3"};
52  private static final Logger LOGGER = Logger.getLogger(FileViewer.class.getName());
53  private Connection connection = null;
54 
55  private String tmpDBPathName = null;
56  private File tmpDBFile = null;
57 
58  private final Map<String, String> dbTablesMap = new TreeMap<>();
59 
60  private static final int ROWS_PER_PAGE = 100;
61  private int numRows; // num of rows in the selected table
62  private int currPage = 0; // curr page of rows being displayed
63 
64  SQLiteTableView selectedTableView = new SQLiteTableView();
65 
66  private SwingWorker<? extends Object, ? extends Object> worker;
67 
71  public SQLiteViewer() {
73  jTableDataPanel.add(selectedTableView, BorderLayout.CENTER);
74  }
75 
81  @SuppressWarnings("unchecked")
82  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
83  private void initComponents() {
84 
85  jHdrPanel = new javax.swing.JPanel();
86  tablesDropdownList = new javax.swing.JComboBox<>();
87  jLabel1 = new javax.swing.JLabel();
88  numEntriesField = new javax.swing.JTextField();
89  jLabel2 = new javax.swing.JLabel();
90  currPageLabel = new javax.swing.JLabel();
91  jLabel3 = new javax.swing.JLabel();
92  numPagesLabel = new javax.swing.JLabel();
93  prevPageButton = new javax.swing.JButton();
94  nextPageButton = new javax.swing.JButton();
95  jTableDataPanel = new javax.swing.JPanel();
96 
97  jHdrPanel.setPreferredSize(new java.awt.Dimension(536, 40));
98 
99  tablesDropdownList.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
100  tablesDropdownList.addActionListener(new java.awt.event.ActionListener() {
101  public void actionPerformed(java.awt.event.ActionEvent evt) {
103  }
104  });
105 
106  org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.jLabel1.text")); // NOI18N
107 
108  numEntriesField.setEditable(false);
109  numEntriesField.setText(org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.numEntriesField.text")); // NOI18N
110  numEntriesField.setBorder(null);
111 
112  org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.jLabel2.text")); // NOI18N
113 
114  org.openide.awt.Mnemonics.setLocalizedText(currPageLabel, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.currPageLabel.text")); // NOI18N
115 
116  org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.jLabel3.text")); // NOI18N
117 
118  org.openide.awt.Mnemonics.setLocalizedText(numPagesLabel, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.numPagesLabel.text")); // NOI18N
119 
120  prevPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N
121  org.openide.awt.Mnemonics.setLocalizedText(prevPageButton, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.prevPageButton.text")); // NOI18N
122  prevPageButton.setBorderPainted(false);
123  prevPageButton.setContentAreaFilled(false);
124  prevPageButton.setDisabledSelectedIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N
125  prevPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
126  prevPageButton.setPreferredSize(new java.awt.Dimension(23, 23));
127  prevPageButton.addActionListener(new java.awt.event.ActionListener() {
128  public void actionPerformed(java.awt.event.ActionEvent evt) {
130  }
131  });
132 
133  nextPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N
134  org.openide.awt.Mnemonics.setLocalizedText(nextPageButton, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.nextPageButton.text")); // NOI18N
135  nextPageButton.setBorderPainted(false);
136  nextPageButton.setContentAreaFilled(false);
137  nextPageButton.setDisabledSelectedIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N
138  nextPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
139  nextPageButton.setPreferredSize(new java.awt.Dimension(23, 23));
140  nextPageButton.addActionListener(new java.awt.event.ActionListener() {
141  public void actionPerformed(java.awt.event.ActionEvent evt) {
143  }
144  });
145 
146  javax.swing.GroupLayout jHdrPanelLayout = new javax.swing.GroupLayout(jHdrPanel);
147  jHdrPanel.setLayout(jHdrPanelLayout);
148  jHdrPanelLayout.setHorizontalGroup(
149  jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
150  .addGroup(jHdrPanelLayout.createSequentialGroup()
151  .addContainerGap()
152  .addComponent(jLabel1)
153  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
154  .addComponent(tablesDropdownList, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
155  .addGap(18, 18, 18)
156  .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE)
157  .addGap(15, 15, 15)
158  .addComponent(jLabel2)
159  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
160  .addComponent(currPageLabel)
161  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
162  .addComponent(jLabel3)
163  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
164  .addComponent(numPagesLabel)
165  .addGap(18, 18, 18)
166  .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
167  .addGap(0, 0, 0)
168  .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
169  .addContainerGap(133, Short.MAX_VALUE))
170  );
171  jHdrPanelLayout.setVerticalGroup(
172  jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
173  .addGroup(jHdrPanelLayout.createSequentialGroup()
174  .addContainerGap()
175  .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
176  .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
177  .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
178  .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
179  .addComponent(tablesDropdownList, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
180  .addComponent(jLabel1)
181  .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
182  .addComponent(jLabel2)
183  .addComponent(currPageLabel)
184  .addComponent(jLabel3)
185  .addComponent(numPagesLabel)))
186  .addContainerGap())
187  );
188 
189  jTableDataPanel.setLayout(new java.awt.BorderLayout());
190 
191  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
192  this.setLayout(layout);
193  layout.setHorizontalGroup(
194  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
195  .addComponent(jHdrPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
196  .addComponent(jTableDataPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
197  );
198  layout.setVerticalGroup(
199  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
200  .addGroup(layout.createSequentialGroup()
201  .addComponent(jHdrPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)
202  .addGap(0, 0, 0)
203  .addComponent(jTableDataPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 317, Short.MAX_VALUE))
204  );
205  }// </editor-fold>//GEN-END:initComponents
206 
207  private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed
208 
209  currPage++;
210  if (currPage * ROWS_PER_PAGE > numRows) {
211  nextPageButton.setEnabled(false);
212  }
213  currPageLabel.setText(Integer.toString(currPage));
214  prevPageButton.setEnabled(true);
215 
216  // read and display a page of rows
217  String tableName = (String) this.tablesDropdownList.getSelectedItem();
218  readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE);
219  }//GEN-LAST:event_nextPageButtonActionPerformed
220 
221  private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed
222 
223  currPage--;
224  if (currPage == 1) {
225  prevPageButton.setEnabled(false);
226  }
227  currPageLabel.setText(Integer.toString(currPage));
228  nextPageButton.setEnabled(true);
229 
230  // read and display a page of rows
231  String tableName = (String) this.tablesDropdownList.getSelectedItem();
232  readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE);
233  }//GEN-LAST:event_prevPageButtonActionPerformed
234 
235  private void tablesDropdownListActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tablesDropdownListActionPerformed
236  JComboBox<?> cb = (JComboBox<?>) evt.getSource();
237  String tableName = (String) cb.getSelectedItem();
238  if (null == tableName) {
239  return;
240  }
241 
242  selectTable(tableName);
243  }//GEN-LAST:event_tablesDropdownListActionPerformed
244 
245 
246  // Variables declaration - do not modify//GEN-BEGIN:variables
247  private javax.swing.JLabel currPageLabel;
248  private javax.swing.JPanel jHdrPanel;
249  private javax.swing.JLabel jLabel1;
250  private javax.swing.JLabel jLabel2;
251  private javax.swing.JLabel jLabel3;
252  private javax.swing.JPanel jTableDataPanel;
253  private javax.swing.JButton nextPageButton;
254  private javax.swing.JTextField numEntriesField;
255  private javax.swing.JLabel numPagesLabel;
256  private javax.swing.JButton prevPageButton;
257  private javax.swing.JComboBox<String> tablesDropdownList;
258  // End of variables declaration//GEN-END:variables
259 
260  @Override
261  public List<String> getSupportedMIMETypes() {
262  return Arrays.asList(SUPPORTED_MIMETYPES);
263  }
264 
265  @Override
266  public void setFile(AbstractFile file) {
267  processSQLiteFile(file);
268  }
269 
270  @Override
271  public Component getComponent() {
272  return this;
273  }
274 
275  @Override
276  public void resetComponent() {
277 
278  dbTablesMap.clear();
279 
280  tablesDropdownList.setEnabled(true);
281  tablesDropdownList.removeAllItems();
282  numEntriesField.setText("");
283 
284  // close DB connection to file
285  if (null != connection) {
286  try {
287  connection.close();
288  connection = null;
289  } catch (SQLException ex) {
290  LOGGER.log(Level.SEVERE, "Failed to close DB connection to file.", ex); //NON-NLS
291  }
292  }
293 
294  // delete last temp file
295  if (null != tmpDBFile) {
296  tmpDBFile.delete();
297  tmpDBFile = null;
298  }
299  }
300 
308  private void processSQLiteFile(AbstractFile sqliteFile) {
309 
310  tablesDropdownList.removeAllItems();
311 
312  new SwingWorker<Boolean, Void>() {
313  @Override
314  protected Boolean doInBackground() throws Exception {
315 
316  try {
317  // Copy the file to temp folder
318  tmpDBPathName = Case.getCurrentCase().getTempDirectory() + File.separator + sqliteFile.getName() + "-" + sqliteFile.getId();
319  tmpDBFile = new File(tmpDBPathName);
320  ContentUtils.writeToFile(sqliteFile, tmpDBFile);
321 
322  // Open copy using JDBC
323  Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver
324  connection = DriverManager.getConnection("jdbc:sqlite:" + tmpDBPathName); //NON-NLS
325 
326  // Read all table names and schema
327  return getTables();
328  } catch (IOException ex) {
329  LOGGER.log(Level.SEVERE, "Failed to copy DB file.", ex); //NON-NLS
330  } catch (SQLException ex) {
331  LOGGER.log(Level.SEVERE, "Failed to Open DB.", ex); //NON-NLS
332  } catch (ClassNotFoundException ex) {
333  LOGGER.log(Level.SEVERE, "Failed to initialize JDBC Sqlite.", ex); //NON-NLS
334  }
335  return false;
336  }
337 
338  @Override
339  protected void done() {
340  super.done();
341  try {
342  boolean status = get();
343  if ((status == true) && (dbTablesMap.size() > 0)) {
344  dbTablesMap.keySet().forEach((tableName) -> {
345  tablesDropdownList.addItem(tableName);
346  });
347  } else {
348  // Populate error message
349  tablesDropdownList.addItem("No tables found");
350  tablesDropdownList.setEnabled(false);
351  }
352  } catch (InterruptedException | ExecutionException ex) {
353  LOGGER.log(Level.SEVERE, "Unexpected exception while opening DB file", ex); //NON-NLS
354  }
355  }
356  }.execute();
357 
358  }
359 
365  private boolean getTables() {
366 
367  try {
368  Statement statement = connection.createStatement();
369 
370  ResultSet resultSet = statement.executeQuery(
371  "SELECT name, sql FROM sqlite_master "
372  + " WHERE type= 'table' "
373  + " ORDER BY name;"); //NON-NLS
374 
375  while (resultSet.next()) {
376  String tableName = resultSet.getString("name"); //NON-NLS
377  String tableSQL = resultSet.getString("sql"); //NON-NLS
378 
379  dbTablesMap.put(tableName, tableSQL);
380  }
381  } catch (SQLException e) {
382  LOGGER.log(Level.SEVERE, "Error getting table names from the DB", e); //NON-NLS
383  }
384  return true;
385  }
386 
387  private void selectTable(String tableName) {
388  if (worker != null && !worker.isDone()) {
389  worker.cancel(false);
390  worker = null;
391  }
392 
393  worker = new SwingWorker<Integer, Void>() {
394  @Override
395  protected Integer doInBackground() throws Exception {
396 
397  try {
398  Statement statement = connection.createStatement();
399  ResultSet resultSet = statement.executeQuery(
400  "SELECT count (*) as count FROM " + tableName); //NON-NLS
401 
402  return resultSet.getInt("count");
403  } catch (SQLException ex) {
404  LOGGER.log(Level.SEVERE, "Failed to get data for table.", ex); //NON-NLS
405  }
406  //NON-NLS
407  return 0;
408  }
409 
410  @Override
411  protected void done() {
412  super.done();
413  try {
414 
415  numRows = get();
416  numEntriesField.setText(numRows + " entries");
417 
418  currPage = 1;
419  currPageLabel.setText(Integer.toString(currPage));
420  numPagesLabel.setText(Integer.toString((numRows / ROWS_PER_PAGE) + 1));
421 
422  prevPageButton.setEnabled(false);
423 
424 
425  if (numRows > 0) {
426  nextPageButton.setEnabled(((numRows > ROWS_PER_PAGE)));
427  readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE);
428  } else {
429  nextPageButton.setEnabled(false);
430  selectedTableView.setupTable(Collections.emptyList());
431  }
432 
433  } catch (InterruptedException | ExecutionException ex) {
434  LOGGER.log(Level.SEVERE, "Unexpected exception while reading table.", ex); //NON-NLS
435  }
436  }
437  };
438  worker.execute();
439  }
440 
441  private void readTable(String tableName, int startRow, int numRowsToRead) {
442 
443  if (worker != null && !worker.isDone()) {
444  worker.cancel(false);
445  worker = null;
446  }
447 
448  worker = new SwingWorker<ArrayList<Map<String, Object>>, Void>() {
449  @Override
450  protected ArrayList<Map<String, Object>> doInBackground() throws Exception {
451  try {
452  Statement statement = connection.createStatement();
453  ResultSet resultSet = statement.executeQuery(
454  "SELECT * FROM " + tableName
455  + " LIMIT " + Integer.toString(numRowsToRead)
456  + " OFFSET " + Integer.toString(startRow - 1)
457  ); //NON-NLS
458 
459  return resultSetToArrayList(resultSet);
460  } catch (SQLException ex) {
461  LOGGER.log(Level.SEVERE, "Failed to get data for table " + tableName, ex); //NON-NLS
462  }
463  //NON-NLS
464  return null;
465  }
466 
467  @Override
468  protected void done() {
469 
470  if (isCancelled()) {
471  return;
472  }
473 
474  super.done();
475  try {
476  ArrayList<Map<String, Object>> rows = get();
477  if (Objects.nonNull(rows)) {
478  selectedTableView.setupTable(rows);
479  }else{
480  selectedTableView.setupTable(Collections.emptyList());
481  }
482  } catch (InterruptedException | ExecutionException ex) {
483  LOGGER.log(Level.SEVERE, "Unexpected exception while reading table " + tableName, ex); //NON-NLS
484  }
485  }
486  };
487 
488  worker.execute();
489  }
490 
491  @NbBundle.Messages("SQLiteViewer.BlobNotShown.message=BLOB Data not shown")
492  private ArrayList<Map<String, Object>> resultSetToArrayList(ResultSet rs) throws SQLException {
493  ResultSetMetaData metaData = rs.getMetaData();
494  int columns = metaData.getColumnCount();
495  ArrayList<Map<String, Object>> rowlist = new ArrayList<>();
496  while (rs.next()) {
497  Map<String, Object> row = new LinkedHashMap<>(columns);
498  for (int i = 1; i <= columns; ++i) {
499  if (rs.getObject(i) == null) {
500  row.put(metaData.getColumnName(i), "");
501  } else {
502  if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) {
503  row.put(metaData.getColumnName(i), Bundle.SQLiteViewer_BlobNotShown_message());
504  } else {
505  row.put(metaData.getColumnName(i), rs.getObject(i));
506  }
507  }
508  }
509  rowlist.add(row);
510  }
511 
512  return rowlist;
513  }
514 }
SwingWorker<?extends Object,?extends Object > worker
ArrayList< Map< String, Object > > resultSetToArrayList(ResultSet rs)
void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt)
void tablesDropdownListActionPerformed(java.awt.event.ActionEvent evt)
void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt)
static< T > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, Future< T > worker, boolean source)
void readTable(String tableName, int startRow, int numRowsToRead)
javax.swing.JComboBox< String > tablesDropdownList
void processSQLiteFile(AbstractFile sqliteFile)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2016 Basis Technology. Generated on: Tue Feb 20 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.