mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-14 17:06:16 +00:00
Merge pull request #5890 from raman-bt/6314-artifact-content-viewer
6382: Define new interface ArtifactContentViewer, and refactor DataContentViewerArtfact
This commit is contained in:
commit
61b7dbe491
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 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.contentviewers;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common interface implemented by artifact viewers.
|
||||||
|
*
|
||||||
|
* An artifact viewer displays the artifact in a custom
|
||||||
|
* layout panel suitable for the artifact type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ArtifactContentViewer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to display the contents of the given artifact.
|
||||||
|
*
|
||||||
|
* @param artifact the artifact to display.
|
||||||
|
*/
|
||||||
|
void setArtifact(BlackboardArtifact artifact);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the panel.
|
||||||
|
*
|
||||||
|
* @return display panel.
|
||||||
|
*/
|
||||||
|
Component getComponent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given artifact is supported by the viewer.
|
||||||
|
*
|
||||||
|
* @param artifact Artifact to check.
|
||||||
|
*
|
||||||
|
* @return True if the artifact can be displayed by the viewer, false otherwise.
|
||||||
|
*/
|
||||||
|
boolean isSupported(BlackboardArtifact artifact);
|
||||||
|
|
||||||
|
}
|
@ -953,4 +953,6 @@ manager.properties.lafError =\
|
|||||||
|
|
||||||
manager.properties.brokenProperty = Broken default property {0} value: {1}
|
manager.properties.brokenProperty = Broken default property {0} value: {1}
|
||||||
|
|
||||||
manager.properties.missingProperty = Missing default property {0} value: {1}
|
manager.properties.missingProperty = Missing default property {0} value: {1}
|
||||||
|
DefaultArtifactContentViewer.copyMenuItem.text=Copy
|
||||||
|
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
|
||||||
|
@ -164,3 +164,5 @@ MediaViewImagePanel.tagsMenu.text_1=\u30bf\u30b0\u30e1\u30cb\u30e5\u30fc
|
|||||||
SQLiteViewer.readTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
|
SQLiteViewer.readTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
|
||||||
# {0} - tableName
|
# {0} - tableName
|
||||||
SQLiteViewer.selectTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u30ab\u30a6\u30f3\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
|
SQLiteViewer.selectTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u30ab\u30a6\u30f3\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
|
||||||
|
DefaultArtifactContentViewer.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e
|
||||||
|
DefaultArtifactContentViewer.copyMenuItem.text=\u30b3\u30d4\u30fc
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||||
|
<NonVisualComponents>
|
||||||
|
<Container class="javax.swing.JPopupMenu" name="rightClickMenu">
|
||||||
|
|
||||||
|
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
|
||||||
|
<Property name="useNullLayout" type="boolean" value="true"/>
|
||||||
|
</Layout>
|
||||||
|
<SubComponents>
|
||||||
|
<MenuItem class="javax.swing.JMenuItem" name="copyMenuItem">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="DefaultArtifactContentViewer.copyMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem class="javax.swing.JMenuItem" name="selectAllMenuItem">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="DefaultArtifactContentViewer.selectAllMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</MenuItem>
|
||||||
|
</SubComponents>
|
||||||
|
</Container>
|
||||||
|
</NonVisualComponents>
|
||||||
|
<Properties>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[100, 58]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||||
|
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||||
|
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||||
|
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||||
|
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||||
|
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||||
|
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||||
|
</AuxValues>
|
||||||
|
|
||||||
|
<Layout>
|
||||||
|
<DimensionLayout dim="0">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Component id="resultsTableScrollPane" pref="100" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</DimensionLayout>
|
||||||
|
<DimensionLayout dim="1">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Component id="resultsTableScrollPane" alignment="1" pref="58" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</DimensionLayout>
|
||||||
|
</Layout>
|
||||||
|
<SubComponents>
|
||||||
|
<Container class="javax.swing.JScrollPane" name="resultsTableScrollPane">
|
||||||
|
<Properties>
|
||||||
|
<Property name="horizontalScrollBarPolicy" type="int" value="32"/>
|
||||||
|
<Property name="verticalScrollBarPolicy" type="int" value="22"/>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[620, 34]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||||
|
</AuxValues>
|
||||||
|
|
||||||
|
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||||
|
</Container>
|
||||||
|
</SubComponents>
|
||||||
|
</Form>
|
@ -0,0 +1,524 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2011-2020 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.contentviewers;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Cursor;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.datatransfer.StringSelection;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.JTextArea;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ListSelectionEvent;
|
||||||
|
import javax.swing.event.TableColumnModelEvent;
|
||||||
|
import javax.swing.table.DefaultTableModel;
|
||||||
|
import javax.swing.table.TableColumn;
|
||||||
|
import javax.swing.event.TableColumnModelListener;
|
||||||
|
import javax.swing.text.View;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
import org.netbeans.swing.etable.ETable;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class displays a Blackboard artifact as a table listing all it's
|
||||||
|
* attributes names and values.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||||
|
public class DefaultArtifactContentViewer extends javax.swing.JPanel implements ArtifactContentViewer {
|
||||||
|
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"DefaultArtifactContentViewer.attrsTableHeader.type=Type",
|
||||||
|
"DefaultArtifactContentViewer.attrsTableHeader.value=Value",
|
||||||
|
"DefaultArtifactContentViewer.attrsTableHeader.sources=Source(s)",
|
||||||
|
"DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database",
|
||||||
|
"DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database"
|
||||||
|
})
|
||||||
|
|
||||||
|
private final static Logger logger = Logger.getLogger(DefaultArtifactContentViewer.class.getName());
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private static final String[] COLUMN_HEADERS = {
|
||||||
|
Bundle.DefaultArtifactContentViewer_attrsTableHeader_type(),
|
||||||
|
Bundle.DefaultArtifactContentViewer_attrsTableHeader_value(),
|
||||||
|
Bundle.DefaultArtifactContentViewer_attrsTableHeader_sources()};
|
||||||
|
private static final int[] COLUMN_WIDTHS = {100, 800, 100};
|
||||||
|
private static final int CELL_BOTTOM_MARGIN = 5;
|
||||||
|
private static final int CELL_RIGHT_MARGIN = 1;
|
||||||
|
|
||||||
|
public DefaultArtifactContentViewer() {
|
||||||
|
initResultsTable();
|
||||||
|
initComponents();
|
||||||
|
resultsTableScrollPane.setViewportView(resultsTable);
|
||||||
|
customizeComponents();
|
||||||
|
resetComponents();
|
||||||
|
resultsTable.setDefaultRenderer(Object.class, new MultiLineTableCellRenderer());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initResultsTable() {
|
||||||
|
resultsTable = new ETable();
|
||||||
|
resultsTable.setModel(new javax.swing.table.DefaultTableModel() {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resultsTable.setCellSelectionEnabled(true);
|
||||||
|
resultsTable.getTableHeader().setReorderingAllowed(false);
|
||||||
|
resultsTable.setColumnHidingAllowed(false);
|
||||||
|
resultsTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION);
|
||||||
|
resultsTable.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void columnAdded(TableColumnModelEvent e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void columnRemoved(TableColumnModelEvent e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void columnMoved(TableColumnModelEvent e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void columnMarginChanged(ChangeEvent e) {
|
||||||
|
updateRowHeights(); //When the user changes column width we may need to resize row height
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void columnSelectionChanged(ListSelectionEvent e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resultsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the row heights to the heights of the content in their Value column.
|
||||||
|
*/
|
||||||
|
private void updateRowHeights() {
|
||||||
|
int valueColIndex = -1;
|
||||||
|
for (int col = 0; col < resultsTable.getColumnCount(); col++) {
|
||||||
|
if (resultsTable.getColumnName(col).equals(COLUMN_HEADERS[1])) {
|
||||||
|
valueColIndex = col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (valueColIndex != -1) {
|
||||||
|
for (int row = 0; row < resultsTable.getRowCount(); row++) {
|
||||||
|
Component comp = resultsTable.prepareRenderer(
|
||||||
|
resultsTable.getCellRenderer(row, valueColIndex), row, valueColIndex);
|
||||||
|
final int rowHeight;
|
||||||
|
if (comp instanceof JTextArea) {
|
||||||
|
final JTextArea tc = (JTextArea) comp;
|
||||||
|
final View rootView = tc.getUI().getRootView(tc);
|
||||||
|
java.awt.Insets i = tc.getInsets();
|
||||||
|
rootView.setSize(resultsTable.getColumnModel().getColumn(valueColIndex)
|
||||||
|
.getWidth() - (i.left + i.right +CELL_RIGHT_MARGIN), //current width minus borders
|
||||||
|
Integer.MAX_VALUE);
|
||||||
|
rowHeight = (int) rootView.getPreferredSpan(View.Y_AXIS);
|
||||||
|
} else {
|
||||||
|
rowHeight = comp.getPreferredSize().height;
|
||||||
|
}
|
||||||
|
if (rowHeight > 0) {
|
||||||
|
resultsTable.setRowHeight(row, rowHeight + CELL_BOTTOM_MARGIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the column widths so that the Value column has most of the space.
|
||||||
|
*/
|
||||||
|
private void updateColumnSizes() {
|
||||||
|
Enumeration<TableColumn> columns = resultsTable.getColumnModel().getColumns();
|
||||||
|
while (columns.hasMoreElements()) {
|
||||||
|
TableColumn col = columns.nextElement();
|
||||||
|
if (col.getHeaderValue().equals(COLUMN_HEADERS[0])) {
|
||||||
|
col.setPreferredWidth(COLUMN_WIDTHS[0]);
|
||||||
|
} else if (col.getHeaderValue().equals(COLUMN_HEADERS[1])) {
|
||||||
|
col.setPreferredWidth(COLUMN_WIDTHS[1]);
|
||||||
|
} else if (col.getHeaderValue().equals(COLUMN_HEADERS[2])) {
|
||||||
|
col.setPreferredWidth(COLUMN_WIDTHS[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called from within the constructor to initialize the form.
|
||||||
|
* WARNING: Do NOT modify this code. The content of this method is always
|
||||||
|
* regenerated by the Form Editor.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||||
|
private void initComponents() {
|
||||||
|
|
||||||
|
rightClickMenu = new javax.swing.JPopupMenu();
|
||||||
|
copyMenuItem = new javax.swing.JMenuItem();
|
||||||
|
selectAllMenuItem = new javax.swing.JMenuItem();
|
||||||
|
resultsTableScrollPane = new javax.swing.JScrollPane();
|
||||||
|
|
||||||
|
copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DefaultArtifactContentViewer.class, "DefaultArtifactContentViewer.copyMenuItem.text")); // NOI18N
|
||||||
|
rightClickMenu.add(copyMenuItem);
|
||||||
|
|
||||||
|
selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DefaultArtifactContentViewer.class, "DefaultArtifactContentViewer.selectAllMenuItem.text")); // NOI18N
|
||||||
|
rightClickMenu.add(selectAllMenuItem);
|
||||||
|
|
||||||
|
setPreferredSize(new java.awt.Dimension(100, 58));
|
||||||
|
|
||||||
|
resultsTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
|
||||||
|
resultsTableScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||||
|
resultsTableScrollPane.setPreferredSize(new java.awt.Dimension(620, 34));
|
||||||
|
|
||||||
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||||
|
this.setLayout(layout);
|
||||||
|
layout.setHorizontalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addComponent(resultsTableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)
|
||||||
|
);
|
||||||
|
layout.setVerticalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addComponent(resultsTableScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 58, Short.MAX_VALUE)
|
||||||
|
);
|
||||||
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
|
private javax.swing.JMenuItem copyMenuItem;
|
||||||
|
private javax.swing.JScrollPane resultsTableScrollPane;
|
||||||
|
private javax.swing.JPopupMenu rightClickMenu;
|
||||||
|
private javax.swing.JMenuItem selectAllMenuItem;
|
||||||
|
// End of variables declaration//GEN-END:variables
|
||||||
|
private ETable resultsTable;
|
||||||
|
|
||||||
|
private void customizeComponents() {
|
||||||
|
resultsTable.setComponentPopupMenu(rightClickMenu);
|
||||||
|
ActionListener actList = new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
JMenuItem jmi = (JMenuItem) e.getSource();
|
||||||
|
if (jmi.equals(copyMenuItem)) {
|
||||||
|
StringBuilder selectedText = new StringBuilder(512);
|
||||||
|
for (int row : resultsTable.getSelectedRows()) {
|
||||||
|
for (int col : resultsTable.getSelectedColumns()) {
|
||||||
|
selectedText.append((String) resultsTable.getValueAt(row, col));
|
||||||
|
selectedText.append('\t');
|
||||||
|
}
|
||||||
|
//if its the last row selected don't add a new line
|
||||||
|
if (row != resultsTable.getSelectedRows()[resultsTable.getSelectedRows().length - 1]) {
|
||||||
|
selectedText.append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(selectedText.toString()), null);
|
||||||
|
} else if (jmi.equals(selectAllMenuItem)) {
|
||||||
|
resultsTable.selectAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
copyMenuItem.addActionListener(actList);
|
||||||
|
|
||||||
|
selectAllMenuItem.addActionListener(actList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the components to an empty view state.
|
||||||
|
*/
|
||||||
|
private void resetComponents() {
|
||||||
|
|
||||||
|
((DefaultTableModel) resultsTable.getModel()).setRowCount(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getComponent() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setArtifact(BlackboardArtifact artifact) {
|
||||||
|
try {
|
||||||
|
ResultsTableArtifact resultsTableArtifact = new ResultsTableArtifact(artifact, artifact.getParent());
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
updateView(resultsTableArtifact);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, String.format("Error getting parent content for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSupported(BlackboardArtifact artifact) {
|
||||||
|
// This viewer supports all artifacts.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is a container to hold the data necessary for the artifact
|
||||||
|
* being viewed.
|
||||||
|
*/
|
||||||
|
private class ResultsTableArtifact {
|
||||||
|
|
||||||
|
private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
|
||||||
|
private String[][] rowData = null;
|
||||||
|
private final String artifactDisplayName;
|
||||||
|
private final Content content;
|
||||||
|
|
||||||
|
ResultsTableArtifact(BlackboardArtifact artifact, Content content) {
|
||||||
|
artifactDisplayName = artifact.getDisplayName();
|
||||||
|
this.content = content;
|
||||||
|
addRows(artifact);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultsTableArtifact(String errorMsg) {
|
||||||
|
artifactDisplayName = errorMsg;
|
||||||
|
rowData = new String[1][3];
|
||||||
|
rowData[0] = new String[]{"", errorMsg, ""};
|
||||||
|
content = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[][] getRows() {
|
||||||
|
return rowData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRows(BlackboardArtifact artifact) {
|
||||||
|
List<String[]> rowsToAdd = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
* Add rows for each attribute.
|
||||||
|
*/
|
||||||
|
for (BlackboardAttribute attr : artifact.getAttributes()) {
|
||||||
|
/*
|
||||||
|
* Attribute value column.
|
||||||
|
*/
|
||||||
|
String value;
|
||||||
|
switch (attr.getAttributeType().getValueType()) {
|
||||||
|
|
||||||
|
// Use Autopsy date formatting settings, not TSK defaults
|
||||||
|
case DATETIME:
|
||||||
|
value = epochTimeToString(attr.getValueLong());
|
||||||
|
break;
|
||||||
|
case JSON:
|
||||||
|
// Get the attribute's JSON value and convert to indented multiline display string
|
||||||
|
String jsonVal = attr.getValueString();
|
||||||
|
JsonParser parser = new JsonParser();
|
||||||
|
JsonObject json = parser.parse(jsonVal).getAsJsonObject();
|
||||||
|
|
||||||
|
value = toJsonDisplayString(json, "");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STRING:
|
||||||
|
case INTEGER:
|
||||||
|
case LONG:
|
||||||
|
case DOUBLE:
|
||||||
|
case BYTE:
|
||||||
|
default:
|
||||||
|
value = attr.getDisplayString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Attribute sources column.
|
||||||
|
*/
|
||||||
|
String sources = StringUtils.join(attr.getSources(), ", ");
|
||||||
|
rowsToAdd.add(new String[]{attr.getAttributeType().getDisplayName(), value, sources});
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Add a row for the source content path.
|
||||||
|
*/
|
||||||
|
String path = "";
|
||||||
|
try {
|
||||||
|
if (null != content) {
|
||||||
|
path = content.getUniquePath();
|
||||||
|
}
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, String.format("Error getting source content path for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
|
||||||
|
path = Bundle.DataContentViewerArtifact_failedToGetSourcePath_message();
|
||||||
|
}
|
||||||
|
rowsToAdd.add(new String[]{"Source File Path", path, ""});
|
||||||
|
/*
|
||||||
|
* Add a row for the artifact id.
|
||||||
|
*/
|
||||||
|
rowsToAdd.add(new String[]{"Artifact ID", Long.toString(artifact.getArtifactID()), ""});
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
rowsToAdd.add(new String[]{"", Bundle.DataContentViewerArtifact_failedToGetAttributes_message(), ""});
|
||||||
|
}
|
||||||
|
rowData = rowsToAdd.toArray(new String[0][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the artifactDisplayName
|
||||||
|
*/
|
||||||
|
String getArtifactDisplayName() {
|
||||||
|
return artifactDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String INDENT_RIGHT = " ";
|
||||||
|
private static final String NEW_LINE = "\n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively converts a JSON element into an indented multi-line
|
||||||
|
* display string.
|
||||||
|
*
|
||||||
|
* @param element JSON element to convert
|
||||||
|
* @param startIndent Starting indentation for the element.
|
||||||
|
*
|
||||||
|
* @return A multi-line display string.
|
||||||
|
*/
|
||||||
|
private String toJsonDisplayString(JsonElement element, String startIndent) {
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder("");
|
||||||
|
JsonObject obj = element.getAsJsonObject();
|
||||||
|
|
||||||
|
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
|
||||||
|
appendJsonElementToString(entry.getKey(), entry.getValue(), startIndent, sb );
|
||||||
|
}
|
||||||
|
|
||||||
|
String returnString = sb.toString();
|
||||||
|
if (startIndent.length() == 0 && returnString.startsWith(NEW_LINE)) {
|
||||||
|
returnString = returnString.substring(NEW_LINE.length());
|
||||||
|
}
|
||||||
|
return returnString;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given JSON element into string and appends to the given string builder.
|
||||||
|
*
|
||||||
|
* @param jsonKey
|
||||||
|
* @param jsonElement
|
||||||
|
* @param startIndent Starting indentation for the element.
|
||||||
|
* @param sb String builder to append to.
|
||||||
|
*/
|
||||||
|
private void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb) {
|
||||||
|
if (jsonElement.isJsonArray()) {
|
||||||
|
JsonArray jsonArray = jsonElement.getAsJsonArray();
|
||||||
|
if (jsonArray.size() > 0) {
|
||||||
|
int count = 1;
|
||||||
|
sb.append(NEW_LINE).append(String.format("%s%s", startIndent, jsonKey));
|
||||||
|
for (JsonElement arrayMember : jsonArray) {
|
||||||
|
sb.append(NEW_LINE).append(String.format("%s%d", startIndent.concat(INDENT_RIGHT), count));
|
||||||
|
sb.append(toJsonDisplayString(arrayMember, startIndent.concat(INDENT_RIGHT).concat(INDENT_RIGHT)));
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (jsonElement.isJsonObject()) {
|
||||||
|
sb.append(NEW_LINE).append(String.format("%s%s %s", startIndent, jsonKey, toJsonDisplayString(jsonElement.getAsJsonObject(), startIndent + INDENT_RIGHT)));
|
||||||
|
} else if (jsonElement.isJsonPrimitive()) {
|
||||||
|
String attributeName = jsonKey;
|
||||||
|
String attributeValue;
|
||||||
|
if (attributeName.toUpperCase().contains("DATETIME")) {
|
||||||
|
attributeValue = epochTimeToString(Long.parseLong(jsonElement.getAsString()));
|
||||||
|
} else {
|
||||||
|
attributeValue = jsonElement.getAsString();
|
||||||
|
}
|
||||||
|
sb.append(NEW_LINE).append(String.format("%s%s = %s", startIndent, attributeName, attributeValue));
|
||||||
|
} else if (jsonElement.isJsonNull()) {
|
||||||
|
sb.append(NEW_LINE).append(String.format("%s%s = null", startIndent, jsonKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts epoch time to readable string.
|
||||||
|
*
|
||||||
|
* @param epochTime epoch time value to be converted to string.
|
||||||
|
* @return String with human readable time.
|
||||||
|
*/
|
||||||
|
private String epochTimeToString(long epochTime) {
|
||||||
|
String dateTimeString = "0000-00-00 00:00:00";
|
||||||
|
if (null != content && 0 != epochTime) {
|
||||||
|
dateFormatter.setTimeZone(ContentUtils.getTimeZone(content));
|
||||||
|
dateTimeString = dateFormatter.format(new java.util.Date(epochTime * 1000));
|
||||||
|
}
|
||||||
|
return dateTimeString;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the table view with the given artifact data.
|
||||||
|
*
|
||||||
|
* It should be called on EDT.
|
||||||
|
*
|
||||||
|
* @param resultsTableArtifact Artifact data to display in the view.
|
||||||
|
*/
|
||||||
|
private void updateView(ResultsTableArtifact resultsTableArtifact) {
|
||||||
|
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
|
|
||||||
|
DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
|
||||||
|
tModel.setDataVector(resultsTableArtifact.getRows(), COLUMN_HEADERS);
|
||||||
|
updateColumnSizes();
|
||||||
|
updateRowHeights();
|
||||||
|
resultsTable.clearSelection();
|
||||||
|
|
||||||
|
this.setCursor(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TableCellRenderer for displaying multiline text.
|
||||||
|
*/
|
||||||
|
private class MultiLineTableCellRenderer implements javax.swing.table.TableCellRenderer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
javax.swing.JTextArea jtex = new javax.swing.JTextArea();
|
||||||
|
if (value instanceof String) {
|
||||||
|
jtex.setText((String) value);
|
||||||
|
jtex.setLineWrap(true);
|
||||||
|
jtex.setWrapStyleWord(false);
|
||||||
|
}
|
||||||
|
//cell backgroud color when selected
|
||||||
|
if (isSelected) {
|
||||||
|
jtex.setBackground(javax.swing.UIManager.getColor("Table.selectionBackground"));
|
||||||
|
} else {
|
||||||
|
jtex.setBackground(javax.swing.UIManager.getColor("Table.background"));
|
||||||
|
}
|
||||||
|
return jtex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,8 +30,6 @@ DataContentViewerArtifact.pageLabel2.text=Result
|
|||||||
DataContentViewerArtifact.nextPageButton.text=
|
DataContentViewerArtifact.nextPageButton.text=
|
||||||
DataContentViewerArtifact.currentPageLabel.text=1
|
DataContentViewerArtifact.currentPageLabel.text=1
|
||||||
DataContentViewerArtifact.ofLabel.text=of
|
DataContentViewerArtifact.ofLabel.text=of
|
||||||
DataContentViewerArtifact.copyMenuItem.text=Copy
|
|
||||||
DataContentViewerArtifact.selectAllMenuItem.text=Select All
|
|
||||||
DataContentViewerArtifact.pageLabel.text=Result:
|
DataContentViewerArtifact.pageLabel.text=Result:
|
||||||
AdvancedConfigurationDialog.applyButton.text=OK
|
AdvancedConfigurationDialog.applyButton.text=OK
|
||||||
DataContentViewerHex.goToPageTextField.text=
|
DataContentViewerHex.goToPageTextField.text=
|
||||||
|
@ -82,8 +82,6 @@ DataContentViewerArtifact.pageLabel2.text=\u7d50\u679c
|
|||||||
DataContentViewerArtifact.nextPageButton.text=
|
DataContentViewerArtifact.nextPageButton.text=
|
||||||
DataContentViewerArtifact.currentPageLabel.text=1
|
DataContentViewerArtifact.currentPageLabel.text=1
|
||||||
DataContentViewerArtifact.ofLabel.text=/
|
DataContentViewerArtifact.ofLabel.text=/
|
||||||
DataContentViewerArtifact.copyMenuItem.text=\u30b3\u30d4\u30fc
|
|
||||||
DataContentViewerArtifact.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e
|
|
||||||
DataContentViewerArtifact.pageLabel.text=\u7d50\u679c:
|
DataContentViewerArtifact.pageLabel.text=\u7d50\u679c:
|
||||||
AdvancedConfigurationDialog.applyButton.text=OK
|
AdvancedConfigurationDialog.applyButton.text=OK
|
||||||
DataContentViewerHex.goToPageTextField.text=
|
DataContentViewerHex.goToPageTextField.text=
|
||||||
|
@ -1,30 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
<Form version="1.9" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||||
<NonVisualComponents>
|
|
||||||
<Container class="javax.swing.JPopupMenu" name="rightClickMenu">
|
|
||||||
|
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
|
|
||||||
<Property name="useNullLayout" type="boolean" value="true"/>
|
|
||||||
</Layout>
|
|
||||||
<SubComponents>
|
|
||||||
<MenuItem class="javax.swing.JMenuItem" name="copyMenuItem">
|
|
||||||
<Properties>
|
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerArtifact.copyMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
|
||||||
</Property>
|
|
||||||
</Properties>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem class="javax.swing.JMenuItem" name="selectAllMenuItem">
|
|
||||||
<Properties>
|
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerArtifact.selectAllMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
|
||||||
</Property>
|
|
||||||
</Properties>
|
|
||||||
</MenuItem>
|
|
||||||
</SubComponents>
|
|
||||||
</Container>
|
|
||||||
</NonVisualComponents>
|
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
<Dimension value="[100, 58]"/>
|
<Dimension value="[100, 58]"/>
|
||||||
@ -45,16 +21,16 @@
|
|||||||
<Layout>
|
<Layout>
|
||||||
<DimensionLayout dim="0">
|
<DimensionLayout dim="0">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Component id="jScrollPane1" max="32767" attributes="0"/>
|
<Component id="jScrollPane1" pref="561" max="32767" attributes="0"/>
|
||||||
<Component id="resultsTableScrollPane" max="32767" attributes="0"/>
|
<Component id="artifactContentPanel" alignment="0" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
<DimensionLayout dim="1">
|
<DimensionLayout dim="1">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" alignment="0" attributes="0">
|
<Group type="102" alignment="0" attributes="0">
|
||||||
<Component id="jScrollPane1" min="-2" pref="24" max="-2" attributes="0"/>
|
<Component id="jScrollPane1" min="-2" pref="24" max="-2" attributes="0"/>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="resultsTableScrollPane" max="32767" attributes="0"/>
|
<Component id="artifactContentPanel" pref="397" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
@ -248,19 +224,34 @@
|
|||||||
</Container>
|
</Container>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Container>
|
</Container>
|
||||||
<Container class="javax.swing.JScrollPane" name="resultsTableScrollPane">
|
<Container class="javax.swing.JPanel" name="artifactContentPanel">
|
||||||
<Properties>
|
<LayoutCode>
|
||||||
<Property name="horizontalScrollBarPolicy" type="int" value="32"/>
|
<CodeStatement>
|
||||||
<Property name="verticalScrollBarPolicy" type="int" value="22"/>
|
<CodeExpression id="1_artifactContentPanel">
|
||||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<CodeVariable name="artifactContentPanel" type="8194" declaredType="javax.swing.JPanel"/>
|
||||||
<Dimension value="[620, 34]"/>
|
<ExpressionOrigin>
|
||||||
</Property>
|
<ExpressionProvider type="ComponentRef">
|
||||||
</Properties>
|
<ComponentRef name="artifactContentPanel"/>
|
||||||
<AuxValues>
|
</ExpressionProvider>
|
||||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
</ExpressionOrigin>
|
||||||
</AuxValues>
|
</CodeExpression>
|
||||||
|
<StatementProvider type="CodeMethod">
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
<CodeMethod name="setLayout" class="java.awt.Container" parameterTypes="java.awt.LayoutManager"/>
|
||||||
|
</StatementProvider>
|
||||||
|
<Parameters>
|
||||||
|
<CodeExpression id="2">
|
||||||
|
<ExpressionOrigin>
|
||||||
|
<ExpressionProvider type="CodeConstructor">
|
||||||
|
<CodeConstructor class="javax.swing.OverlayLayout" parameterTypes="java.awt.Container"/>
|
||||||
|
</ExpressionProvider>
|
||||||
|
<Parameters>
|
||||||
|
<CodeExpression id="1_artifactContentPanel"/>
|
||||||
|
</Parameters>
|
||||||
|
</ExpressionOrigin>
|
||||||
|
</CodeExpression>
|
||||||
|
</Parameters>
|
||||||
|
</CodeStatement>
|
||||||
|
</LayoutCode>
|
||||||
</Container>
|
</Container>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -20,178 +20,67 @@ package org.sleuthkit.autopsy.corecomponents;
|
|||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Cursor;
|
import java.awt.Cursor;
|
||||||
import java.awt.Toolkit;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.datatransfer.StringSelection;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javax.swing.JMenuItem;
|
|
||||||
import javax.swing.JTextArea;
|
|
||||||
import javax.swing.SwingWorker;
|
import javax.swing.SwingWorker;
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ListSelectionEvent;
|
|
||||||
import javax.swing.event.TableColumnModelEvent;
|
|
||||||
import javax.swing.table.DefaultTableModel;
|
|
||||||
import javax.swing.table.TableColumn;
|
|
||||||
import javax.swing.event.TableColumnModelListener;
|
|
||||||
import javax.swing.text.View;
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.openide.util.Lookup;
|
import org.openide.util.Lookup;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.datamodel.TskException;
|
import org.sleuthkit.datamodel.TskException;
|
||||||
import org.netbeans.swing.etable.ETable;
|
import java.util.Arrays;
|
||||||
import com.google.gson.JsonElement;
|
import java.util.Collections;
|
||||||
import com.google.gson.JsonObject;
|
import org.sleuthkit.autopsy.contentviewers.ArtifactContentViewer;
|
||||||
import com.google.gson.JsonParser;
|
import org.sleuthkit.autopsy.contentviewers.DefaultArtifactContentViewer;
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instances of this class display the BlackboardArtifacts associated with the
|
* Instances of this class display the BlackboardArtifacts associated with the
|
||||||
* Content represented by a Node. Each BlackboardArtifact is rendered displayed
|
* Content represented by a Node.
|
||||||
* in a JTable representation of its BlackboardAttributes.
|
*
|
||||||
|
* It goes through a list of known ArtifactContentViewer to find a viewer that
|
||||||
|
* supports a given artifact and then hands it the artifact to display.
|
||||||
*/
|
*/
|
||||||
@ServiceProvider(service = DataContentViewer.class, position = 7)
|
@ServiceProvider(service = DataContentViewer.class, position = 7)
|
||||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||||
public class DataContentViewerArtifact extends javax.swing.JPanel implements DataContentViewer {
|
public class DataContentViewerArtifact extends javax.swing.JPanel implements DataContentViewer {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@NbBundle.Messages({
|
@NbBundle.Messages({
|
||||||
"DataContentViewerArtifact.attrsTableHeader.type=Type",
|
|
||||||
"DataContentViewerArtifact.attrsTableHeader.value=Value",
|
|
||||||
"DataContentViewerArtifact.attrsTableHeader.sources=Source(s)",
|
|
||||||
"DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database",
|
"DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database",
|
||||||
"DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database"
|
"DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database"
|
||||||
})
|
})
|
||||||
private final static Logger logger = Logger.getLogger(DataContentViewerArtifact.class.getName());
|
private final static Logger logger = Logger.getLogger(DataContentViewerArtifact.class.getName());
|
||||||
private final static String WAIT_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.waitText");
|
private final static String WAIT_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.waitText");
|
||||||
private final static String ERROR_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.errorText");
|
private final static String ERROR_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.errorText");
|
||||||
|
|
||||||
private Node currentNode; // @@@ Remove this when the redundant setNode() calls problem is fixed.
|
private Node currentNode; // @@@ Remove this when the redundant setNode() calls problem is fixed.
|
||||||
private int currentPage = 1;
|
private int currentPage = 1;
|
||||||
private final Object lock = new Object();
|
private final Object lock = new Object();
|
||||||
private List<ResultsTableArtifact> artifactTableContents; // Accessed by multiple threads, use getArtifactContents() and setArtifactContents()
|
private List<BlackboardArtifact> artifactTableContents; // Accessed by multiple threads, use getArtifactContents() and setArtifactContents()
|
||||||
SwingWorker<ViewUpdate, Void> currentTask; // Accessed by multiple threads, use startNewTask()
|
private SwingWorker<ViewUpdate, Void> currentTask; // Accessed by multiple threads, use startNewTask()
|
||||||
private static final String[] COLUMN_HEADERS = {
|
|
||||||
Bundle.DataContentViewerArtifact_attrsTableHeader_type(),
|
|
||||||
Bundle.DataContentViewerArtifact_attrsTableHeader_value(),
|
|
||||||
Bundle.DataContentViewerArtifact_attrsTableHeader_sources()};
|
|
||||||
private static final int[] COLUMN_WIDTHS = {100, 800, 100};
|
|
||||||
private static final int CELL_BOTTOM_MARGIN = 5;
|
|
||||||
private static final int CELL_RIGHT_MARGIN = 1;
|
|
||||||
|
|
||||||
|
private final Collection<ArtifactContentViewer> KNOWN_ARTIFACT_VIEWERS =
|
||||||
|
Arrays.asList(
|
||||||
|
// TBD: populate with custom artifact viewers.
|
||||||
|
);
|
||||||
|
|
||||||
public DataContentViewerArtifact() {
|
public DataContentViewerArtifact() {
|
||||||
initResultsTable();
|
|
||||||
initComponents();
|
initComponents();
|
||||||
resultsTableScrollPane.setViewportView(resultsTable);
|
|
||||||
customizeComponents();
|
|
||||||
resetComponents();
|
resetComponents();
|
||||||
resultsTable.setDefaultRenderer(Object.class, new MultiLineTableCellRenderer());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initResultsTable() {
|
|
||||||
resultsTable = new ETable();
|
|
||||||
resultsTable.setModel(new javax.swing.table.DefaultTableModel() {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
resultsTable.setCellSelectionEnabled(true);
|
|
||||||
resultsTable.getTableHeader().setReorderingAllowed(false);
|
|
||||||
resultsTable.setColumnHidingAllowed(false);
|
|
||||||
resultsTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION);
|
|
||||||
resultsTable.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void columnAdded(TableColumnModelEvent e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void columnRemoved(TableColumnModelEvent e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void columnMoved(TableColumnModelEvent e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void columnMarginChanged(ChangeEvent e) {
|
|
||||||
updateRowHeights(); //When the user changes column width we may need to resize row height
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void columnSelectionChanged(ListSelectionEvent e) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
resultsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the row heights to the heights of the content in their Value column.
|
|
||||||
*/
|
|
||||||
private void updateRowHeights() {
|
|
||||||
int valueColIndex = -1;
|
|
||||||
for (int col = 0; col < resultsTable.getColumnCount(); col++) {
|
|
||||||
if (resultsTable.getColumnName(col).equals(COLUMN_HEADERS[1])) {
|
|
||||||
valueColIndex = col;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (valueColIndex != -1) {
|
|
||||||
for (int row = 0; row < resultsTable.getRowCount(); row++) {
|
|
||||||
Component comp = resultsTable.prepareRenderer(
|
|
||||||
resultsTable.getCellRenderer(row, valueColIndex), row, valueColIndex);
|
|
||||||
final int rowHeight;
|
|
||||||
if (comp instanceof JTextArea) {
|
|
||||||
final JTextArea tc = (JTextArea) comp;
|
|
||||||
final View rootView = tc.getUI().getRootView(tc);
|
|
||||||
java.awt.Insets i = tc.getInsets();
|
|
||||||
rootView.setSize(resultsTable.getColumnModel().getColumn(valueColIndex)
|
|
||||||
.getWidth() - (i.left + i.right +CELL_RIGHT_MARGIN), //current width minus borders
|
|
||||||
Integer.MAX_VALUE);
|
|
||||||
rowHeight = (int) rootView.getPreferredSpan(View.Y_AXIS);
|
|
||||||
} else {
|
|
||||||
rowHeight = comp.getPreferredSize().height;
|
|
||||||
}
|
|
||||||
if (rowHeight > 0) {
|
|
||||||
resultsTable.setRowHeight(row, rowHeight + CELL_BOTTOM_MARGIN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the column widths so that the Value column has most of the space.
|
|
||||||
*/
|
|
||||||
private void updateColumnSizes() {
|
|
||||||
Enumeration<TableColumn> columns = resultsTable.getColumnModel().getColumns();
|
|
||||||
while (columns.hasMoreElements()) {
|
|
||||||
TableColumn col = columns.nextElement();
|
|
||||||
if (col.getHeaderValue().equals(COLUMN_HEADERS[0])) {
|
|
||||||
col.setPreferredWidth(COLUMN_WIDTHS[0]);
|
|
||||||
} else if (col.getHeaderValue().equals(COLUMN_HEADERS[1])) {
|
|
||||||
col.setPreferredWidth(COLUMN_WIDTHS[1]);
|
|
||||||
} else if (col.getHeaderValue().equals(COLUMN_HEADERS[2])) {
|
|
||||||
col.setPreferredWidth(COLUMN_WIDTHS[2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -204,9 +93,6 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
java.awt.GridBagConstraints gridBagConstraints;
|
java.awt.GridBagConstraints gridBagConstraints;
|
||||||
|
|
||||||
rightClickMenu = new javax.swing.JPopupMenu();
|
|
||||||
copyMenuItem = new javax.swing.JMenuItem();
|
|
||||||
selectAllMenuItem = new javax.swing.JMenuItem();
|
|
||||||
jScrollPane1 = new javax.swing.JScrollPane();
|
jScrollPane1 = new javax.swing.JScrollPane();
|
||||||
jPanel1 = new javax.swing.JPanel();
|
jPanel1 = new javax.swing.JPanel();
|
||||||
totalPageLabel = new javax.swing.JLabel();
|
totalPageLabel = new javax.swing.JLabel();
|
||||||
@ -218,13 +104,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
prevPageButton = new javax.swing.JButton();
|
prevPageButton = new javax.swing.JButton();
|
||||||
artifactLabel = new javax.swing.JLabel();
|
artifactLabel = new javax.swing.JLabel();
|
||||||
filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0));
|
filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0));
|
||||||
resultsTableScrollPane = new javax.swing.JScrollPane();
|
artifactContentPanel = new javax.swing.JPanel();
|
||||||
|
|
||||||
copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.copyMenuItem.text")); // NOI18N
|
|
||||||
rightClickMenu.add(copyMenuItem);
|
|
||||||
|
|
||||||
selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.selectAllMenuItem.text")); // NOI18N
|
|
||||||
rightClickMenu.add(selectAllMenuItem);
|
|
||||||
|
|
||||||
setPreferredSize(new java.awt.Dimension(100, 58));
|
setPreferredSize(new java.awt.Dimension(100, 58));
|
||||||
|
|
||||||
@ -341,43 +221,41 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
|
|
||||||
jScrollPane1.setViewportView(jPanel1);
|
jScrollPane1.setViewportView(jPanel1);
|
||||||
|
|
||||||
resultsTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
|
artifactContentPanel.setLayout(new javax.swing.OverlayLayout(artifactContentPanel));
|
||||||
resultsTableScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
|
||||||
resultsTableScrollPane.setPreferredSize(new java.awt.Dimension(620, 34));
|
|
||||||
|
|
||||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||||
this.setLayout(layout);
|
this.setLayout(layout);
|
||||||
layout.setHorizontalGroup(
|
layout.setHorizontalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addComponent(jScrollPane1)
|
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 561, Short.MAX_VALUE)
|
||||||
.addComponent(resultsTableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
.addComponent(artifactContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
);
|
);
|
||||||
layout.setVerticalGroup(
|
layout.setVerticalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(layout.createSequentialGroup()
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(resultsTableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
.addComponent(artifactContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 397, Short.MAX_VALUE))
|
||||||
);
|
);
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed
|
private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed
|
||||||
currentPage = currentPage + 1;
|
currentPage += 1;
|
||||||
currentPageLabel.setText(Integer.toString(currentPage));
|
currentPageLabel.setText(Integer.toString(currentPage));
|
||||||
artifactLabel.setText(artifactTableContents.get(currentPage - 1).getArtifactDisplayName());
|
artifactLabel.setText(artifactTableContents.get(currentPage - 1).getDisplayName());
|
||||||
startNewTask(new SelectedArtifactChangedTask(currentPage));
|
startNewTask(new SelectedArtifactChangedTask(currentPage));
|
||||||
}//GEN-LAST:event_nextPageButtonActionPerformed
|
}//GEN-LAST:event_nextPageButtonActionPerformed
|
||||||
|
|
||||||
private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed
|
private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed
|
||||||
currentPage = currentPage - 1;
|
currentPage -= 1;
|
||||||
currentPageLabel.setText(Integer.toString(currentPage));
|
currentPageLabel.setText(Integer.toString(currentPage));
|
||||||
artifactLabel.setText(artifactTableContents.get(currentPage - 1).getArtifactDisplayName());
|
artifactLabel.setText(artifactTableContents.get(currentPage - 1).getDisplayName());
|
||||||
startNewTask(new SelectedArtifactChangedTask(currentPage));
|
startNewTask(new SelectedArtifactChangedTask(currentPage));
|
||||||
}//GEN-LAST:event_prevPageButtonActionPerformed
|
}//GEN-LAST:event_prevPageButtonActionPerformed
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
|
private javax.swing.JPanel artifactContentPanel;
|
||||||
private javax.swing.JLabel artifactLabel;
|
private javax.swing.JLabel artifactLabel;
|
||||||
private javax.swing.JMenuItem copyMenuItem;
|
|
||||||
private javax.swing.JLabel currentPageLabel;
|
private javax.swing.JLabel currentPageLabel;
|
||||||
private javax.swing.Box.Filler filler1;
|
private javax.swing.Box.Filler filler1;
|
||||||
private javax.swing.JPanel jPanel1;
|
private javax.swing.JPanel jPanel1;
|
||||||
@ -387,41 +265,9 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
private javax.swing.JLabel pageLabel;
|
private javax.swing.JLabel pageLabel;
|
||||||
private javax.swing.JLabel pageLabel2;
|
private javax.swing.JLabel pageLabel2;
|
||||||
private javax.swing.JButton prevPageButton;
|
private javax.swing.JButton prevPageButton;
|
||||||
private javax.swing.JScrollPane resultsTableScrollPane;
|
|
||||||
private javax.swing.JPopupMenu rightClickMenu;
|
|
||||||
private javax.swing.JMenuItem selectAllMenuItem;
|
|
||||||
private javax.swing.JLabel totalPageLabel;
|
private javax.swing.JLabel totalPageLabel;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
private ETable resultsTable;
|
|
||||||
|
|
||||||
private void customizeComponents() {
|
|
||||||
resultsTable.setComponentPopupMenu(rightClickMenu);
|
|
||||||
ActionListener actList = new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
JMenuItem jmi = (JMenuItem) e.getSource();
|
|
||||||
if (jmi.equals(copyMenuItem)) {
|
|
||||||
StringBuilder selectedText = new StringBuilder(512);
|
|
||||||
for (int row : resultsTable.getSelectedRows()) {
|
|
||||||
for (int col : resultsTable.getSelectedColumns()) {
|
|
||||||
selectedText.append((String) resultsTable.getValueAt(row, col));
|
|
||||||
selectedText.append("\t");
|
|
||||||
}
|
|
||||||
//if its the last row selected don't add a new line
|
|
||||||
if (row != resultsTable.getSelectedRows()[resultsTable.getSelectedRows().length - 1]) {
|
|
||||||
selectedText.append(System.lineSeparator());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(selectedText.toString()), null);
|
|
||||||
} else if (jmi.equals(selectAllMenuItem)) {
|
|
||||||
resultsTable.selectAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
copyMenuItem.addActionListener(actList);
|
|
||||||
|
|
||||||
selectAllMenuItem.addActionListener(actList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the components to an empty view state.
|
* Resets the components to an empty view state.
|
||||||
@ -431,10 +277,12 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
currentPageLabel.setText("");
|
currentPageLabel.setText("");
|
||||||
artifactLabel.setText("");
|
artifactLabel.setText("");
|
||||||
totalPageLabel.setText("");
|
totalPageLabel.setText("");
|
||||||
((DefaultTableModel) resultsTable.getModel()).setRowCount(0);
|
|
||||||
prevPageButton.setEnabled(false);
|
prevPageButton.setEnabled(false);
|
||||||
nextPageButton.setEnabled(false);
|
nextPageButton.setEnabled(false);
|
||||||
currentNode = null;
|
currentNode = null;
|
||||||
|
|
||||||
|
artifactContentPanel.removeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -521,183 +369,13 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private ArtifactContentViewer getSupportingViewer(BlackboardArtifact artifact) {
|
||||||
* This class is a container to hold the data necessary for each of the
|
|
||||||
* result pages associated with file or artifact beivng viewed.
|
|
||||||
*/
|
|
||||||
private class ResultsTableArtifact {
|
|
||||||
|
|
||||||
private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
||||||
private String[][] rowData = null;
|
|
||||||
private final String artifactDisplayName;
|
|
||||||
private final Content content;
|
|
||||||
|
|
||||||
ResultsTableArtifact(BlackboardArtifact artifact, Content content) {
|
|
||||||
artifactDisplayName = artifact.getDisplayName();
|
|
||||||
this.content = content;
|
|
||||||
addRows(artifact);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultsTableArtifact(String errorMsg) {
|
|
||||||
artifactDisplayName = errorMsg;
|
|
||||||
rowData = new String[1][3];
|
|
||||||
rowData[0] = new String[]{"", errorMsg, ""};
|
|
||||||
content = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[][] getRows() {
|
|
||||||
return rowData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addRows(BlackboardArtifact artifact) {
|
|
||||||
List<String[]> rowsToAdd = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
/*
|
|
||||||
* Add rows for each attribute.
|
|
||||||
*/
|
|
||||||
for (BlackboardAttribute attr : artifact.getAttributes()) {
|
|
||||||
/*
|
|
||||||
* Attribute value column.
|
|
||||||
*/
|
|
||||||
String value = "";
|
|
||||||
switch (attr.getAttributeType().getValueType()) {
|
|
||||||
case STRING:
|
|
||||||
case INTEGER:
|
|
||||||
case LONG:
|
|
||||||
case DOUBLE:
|
|
||||||
case BYTE:
|
|
||||||
default:
|
|
||||||
value = attr.getDisplayString();
|
|
||||||
break;
|
|
||||||
// Use Autopsy date formatting settings, not TSK defaults
|
|
||||||
case DATETIME:
|
|
||||||
value = epochTimeToString(attr.getValueLong());
|
|
||||||
break;
|
|
||||||
case JSON:
|
|
||||||
// Get the attribute's JSON value and convert to indented multiline display string
|
|
||||||
String jsonVal = attr.getValueString();
|
|
||||||
JsonParser parser = new JsonParser();
|
|
||||||
JsonObject json = parser.parse(jsonVal).getAsJsonObject();
|
|
||||||
|
|
||||||
value = toJsonDisplayString(json, "");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Attribute sources column.
|
|
||||||
*/
|
|
||||||
String sources = StringUtils.join(attr.getSources(), ", ");
|
|
||||||
rowsToAdd.add(new String[]{attr.getAttributeType().getDisplayName(), value, sources});
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Add a row for the source content path.
|
|
||||||
*/
|
|
||||||
String path = "";
|
|
||||||
try {
|
|
||||||
if (null != content) {
|
|
||||||
path = content.getUniquePath();
|
|
||||||
}
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, String.format("Error getting source content path for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
|
|
||||||
path = Bundle.DataContentViewerArtifact_failedToGetSourcePath_message();
|
|
||||||
}
|
|
||||||
rowsToAdd.add(new String[]{"Source File Path", path, ""});
|
|
||||||
/*
|
|
||||||
* Add a row for the artifact id.
|
|
||||||
*/
|
|
||||||
rowsToAdd.add(new String[]{"Artifact ID", Long.toString(artifact.getArtifactID()), ""});
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
rowsToAdd.add(new String[]{"", Bundle.DataContentViewerArtifact_failedToGetAttributes_message(), ""});
|
|
||||||
}
|
|
||||||
rowData = rowsToAdd.toArray(new String[0][0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the artifactDisplayName
|
|
||||||
*/
|
|
||||||
String getArtifactDisplayName() {
|
|
||||||
return artifactDisplayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String INDENT_RIGHT = " ";
|
return this.KNOWN_ARTIFACT_VIEWERS.stream()
|
||||||
private static final String NEW_LINE = "\n";
|
.filter(knownViewer -> knownViewer.isSupported(artifact))
|
||||||
|
.findAny()
|
||||||
/**
|
.orElse(new DefaultArtifactContentViewer());
|
||||||
* Recursively converts a JSON element into an indented multi-line
|
|
||||||
* display string.
|
|
||||||
*
|
|
||||||
* @param element JSON element to convert
|
|
||||||
* @param startIndent Starting indentation for the element.
|
|
||||||
*
|
|
||||||
* @return A multi-line display string.
|
|
||||||
*/
|
|
||||||
private String toJsonDisplayString(JsonElement element, String startIndent) {
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder("");
|
|
||||||
JsonObject obj = element.getAsJsonObject();
|
|
||||||
|
|
||||||
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
|
|
||||||
appendJsonElementToString(entry.getKey(), entry.getValue(), startIndent, sb );
|
|
||||||
}
|
|
||||||
|
|
||||||
String returnString = sb.toString();
|
|
||||||
if (startIndent.length() == 0 && returnString.startsWith(NEW_LINE)) {
|
|
||||||
returnString = returnString.substring(NEW_LINE.length());
|
|
||||||
}
|
|
||||||
return returnString;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the given JSON element into string and appends to the given string builder.
|
|
||||||
*
|
|
||||||
* @param jsonKey
|
|
||||||
* @param jsonElement
|
|
||||||
* @param startIndent Starting indentation for the element.
|
|
||||||
* @param sb String builder to append to.
|
|
||||||
*/
|
|
||||||
private void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb) {
|
|
||||||
if (jsonElement.isJsonArray()) {
|
|
||||||
JsonArray jsonArray = jsonElement.getAsJsonArray();
|
|
||||||
if (jsonArray.size() > 0) {
|
|
||||||
int count = 1;
|
|
||||||
sb.append(NEW_LINE).append(String.format("%s%s", startIndent, jsonKey));
|
|
||||||
for (JsonElement arrayMember : jsonArray) {
|
|
||||||
sb.append(NEW_LINE).append(String.format("%s%d", startIndent.concat(INDENT_RIGHT), count));
|
|
||||||
sb.append(toJsonDisplayString(arrayMember, startIndent.concat(INDENT_RIGHT).concat(INDENT_RIGHT)));
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (jsonElement.isJsonObject()) {
|
|
||||||
sb.append(NEW_LINE).append(String.format("%s%s %s", startIndent, jsonKey, toJsonDisplayString(jsonElement.getAsJsonObject(), startIndent + INDENT_RIGHT)));
|
|
||||||
} else if (jsonElement.isJsonPrimitive()) {
|
|
||||||
String attributeName = jsonKey;
|
|
||||||
String attributeValue;
|
|
||||||
if (attributeName.toUpperCase().contains("DATETIME")) {
|
|
||||||
attributeValue = epochTimeToString(Long.parseLong(jsonElement.getAsString()));
|
|
||||||
} else {
|
|
||||||
attributeValue = jsonElement.getAsString();
|
|
||||||
}
|
|
||||||
sb.append(NEW_LINE).append(String.format("%s%s = %s", startIndent, attributeName, attributeValue));
|
|
||||||
} else if (jsonElement.isJsonNull()) {
|
|
||||||
sb.append(NEW_LINE).append(String.format("%s%s = null", startIndent, jsonKey));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts epoch time to readable string.
|
|
||||||
*
|
|
||||||
* @param epochTime epoch time value to be converted to string.
|
|
||||||
* @return String with human readable time.
|
|
||||||
*/
|
|
||||||
private String epochTimeToString(long epochTime) {
|
|
||||||
String dateTimeString = "0000-00-00 00:00:00";
|
|
||||||
if (null != content && 0 != epochTime) {
|
|
||||||
dateFormatter.setTimeZone(ContentUtils.getTimeZone(content));
|
|
||||||
dateTimeString = dateFormatter.format(new java.util.Date(epochTime * 1000));
|
|
||||||
}
|
|
||||||
return dateTimeString;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -708,18 +386,21 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
|
|
||||||
int numberOfPages;
|
int numberOfPages;
|
||||||
int currentPage;
|
int currentPage;
|
||||||
ResultsTableArtifact tableContents;
|
BlackboardArtifact artifact;
|
||||||
|
String errorMsg;
|
||||||
|
|
||||||
ViewUpdate(int numberOfPages, int currentPage, ResultsTableArtifact contents) {
|
ViewUpdate(int numberOfPages, int currentPage, BlackboardArtifact artifact) {
|
||||||
this.currentPage = currentPage;
|
this.currentPage = currentPage;
|
||||||
this.numberOfPages = numberOfPages;
|
this.numberOfPages = numberOfPages;
|
||||||
this.tableContents = contents;
|
this.artifact = artifact;
|
||||||
|
this.errorMsg = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewUpdate(int numberOfPages, int currentPage, String errorMsg) {
|
ViewUpdate(int numberOfPages, int currentPage, String errorMsg) {
|
||||||
this.currentPage = currentPage;
|
this.currentPage = currentPage;
|
||||||
this.numberOfPages = numberOfPages;
|
this.numberOfPages = numberOfPages;
|
||||||
this.tableContents = new ResultsTableArtifact(errorMsg);
|
this.errorMsg = errorMsg;
|
||||||
|
this.artifact = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -738,14 +419,26 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
currentPage = viewUpdate.currentPage;
|
currentPage = viewUpdate.currentPage;
|
||||||
totalPageLabel.setText(Integer.toString(viewUpdate.numberOfPages));
|
totalPageLabel.setText(Integer.toString(viewUpdate.numberOfPages));
|
||||||
currentPageLabel.setText(Integer.toString(currentPage));
|
currentPageLabel.setText(Integer.toString(currentPage));
|
||||||
artifactLabel.setText(viewUpdate.tableContents.getArtifactDisplayName());
|
|
||||||
DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
|
|
||||||
tModel.setDataVector(viewUpdate.tableContents.getRows(), COLUMN_HEADERS);
|
artifactContentPanel.removeAll();
|
||||||
updateColumnSizes();
|
|
||||||
updateRowHeights();
|
if (viewUpdate.artifact != null) {
|
||||||
resultsTable.clearSelection();
|
artifactLabel.setText(viewUpdate.artifact.getDisplayName());
|
||||||
|
|
||||||
|
BlackboardArtifact artifact = viewUpdate.artifact;
|
||||||
|
ArtifactContentViewer viewer = this.getSupportingViewer(artifact);
|
||||||
|
viewer.setArtifact(artifact);
|
||||||
|
|
||||||
|
artifactContentPanel.add(viewer.getComponent());
|
||||||
|
} else {
|
||||||
|
artifactLabel.setText(viewUpdate.errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
artifactContentPanel.revalidate();
|
||||||
this.setCursor(null);
|
this.setCursor(null);
|
||||||
|
|
||||||
|
this.revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -755,13 +448,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
* @param task A new SwingWorker object to execute as a background thread.
|
* @param task A new SwingWorker object to execute as a background thread.
|
||||||
*/
|
*/
|
||||||
private synchronized void startNewTask(SwingWorker<ViewUpdate, Void> task) {
|
private synchronized void startNewTask(SwingWorker<ViewUpdate, Void> task) {
|
||||||
String[][] waitRow = new String[1][3];
|
|
||||||
waitRow[0] = new String[]{"", WAIT_TEXT, ""};
|
|
||||||
DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
|
|
||||||
tModel.setDataVector(waitRow, COLUMN_HEADERS);
|
|
||||||
updateColumnSizes();
|
|
||||||
updateRowHeights();
|
|
||||||
resultsTable.clearSelection();
|
|
||||||
// The output of the previous task is no longer relevant.
|
// The output of the previous task is no longer relevant.
|
||||||
if (currentTask != null) {
|
if (currentTask != null) {
|
||||||
// This call sets a cancellation flag. It does not terminate the background thread running the task.
|
// This call sets a cancellation flag. It does not terminate the background thread running the task.
|
||||||
@ -775,12 +462,12 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populate the cache of artifact represented as ResultsTableArtifacts.
|
* Populate the cache of artifacts represented as ResultsTableArtifacts.
|
||||||
*
|
*
|
||||||
* @param artifactList A list of ResultsTableArtifact representations of
|
* @param artifactList A list of ResultsTableArtifact representations of
|
||||||
* artifacts.
|
* artifacts.
|
||||||
*/
|
*/
|
||||||
private void setArtifactContents(List<ResultsTableArtifact> artifactList) {
|
private void setArtifactContents(List<BlackboardArtifact> artifactList) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
this.artifactTableContents = artifactList;
|
this.artifactTableContents = artifactList;
|
||||||
}
|
}
|
||||||
@ -789,11 +476,11 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
/**
|
/**
|
||||||
* Retrieve the cache of artifact represented as ResultsTableArtifacts.
|
* Retrieve the cache of artifact represented as ResultsTableArtifacts.
|
||||||
*
|
*
|
||||||
* @return A list of ResultsTableArtifact representations of artifacts.
|
* @return A list of artifacts.
|
||||||
*/
|
*/
|
||||||
private List<ResultsTableArtifact> getArtifactContents() {
|
private List<BlackboardArtifact> getArtifactContents() {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
return artifactTableContents;
|
return Collections.unmodifiableList(artifactTableContents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -843,9 +530,9 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build the new artifact contents cache.
|
// Build the new artifact contents cache.
|
||||||
ArrayList<ResultsTableArtifact> artifactContents = new ArrayList<>();
|
ArrayList<BlackboardArtifact> artifactContents = new ArrayList<>();
|
||||||
for (BlackboardArtifact artifact : artifacts) {
|
for (BlackboardArtifact artifact : artifacts) {
|
||||||
artifactContents.add(new ResultsTableArtifact(artifact, underlyingContent));
|
artifactContents.add(artifact);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the node has an underlying blackboard artifact, show it. If not,
|
// If the node has an underlying blackboard artifact, show it. If not,
|
||||||
@ -932,14 +619,14 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
protected ViewUpdate doInBackground() {
|
protected ViewUpdate doInBackground() {
|
||||||
// Get the artifact content to display from the cache. Note that one must be subtracted from the
|
// Get the artifact content to display from the cache. Note that one must be subtracted from the
|
||||||
// page index to get the corresponding artifact content index.
|
// page index to get the corresponding artifact content index.
|
||||||
List<ResultsTableArtifact> artifactContents = getArtifactContents();
|
List<BlackboardArtifact> artifactContents = getArtifactContents();
|
||||||
ResultsTableArtifact artifactContent = artifactContents.get(pageIndex - 1);
|
|
||||||
|
|
||||||
// It may take a considerable amount of time to fetch the attributes of the selected artifact so check for cancellation.
|
// It may take a considerable amount of time to fetch the attributes of the selected artifact so check for cancellation.
|
||||||
if (isCancelled()) {
|
if (isCancelled()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlackboardArtifact artifactContent = artifactContents.get(pageIndex - 1);
|
||||||
return new ViewUpdate(artifactContents.size(), pageIndex, artifactContent);
|
return new ViewUpdate(artifactContents.size(), pageIndex, artifactContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -957,27 +644,4 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TableCellRenderer for displaying multiline text.
|
|
||||||
*/
|
|
||||||
private class MultiLineTableCellRenderer implements javax.swing.table.TableCellRenderer {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
|
||||||
javax.swing.JTextArea jtex = new javax.swing.JTextArea();
|
|
||||||
if (value instanceof String) {
|
|
||||||
jtex.setText((String) value);
|
|
||||||
jtex.setLineWrap(true);
|
|
||||||
jtex.setWrapStyleWord(false);
|
|
||||||
}
|
|
||||||
//cell backgroud color when selected
|
|
||||||
if (isSelected) {
|
|
||||||
jtex.setBackground(javax.swing.UIManager.getColor("Table.selectionBackground"));
|
|
||||||
} else {
|
|
||||||
jtex.setBackground(javax.swing.UIManager.getColor("Table.background"));
|
|
||||||
}
|
|
||||||
return jtex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user