Merge branch 'develop' of https://github.com/sleuthkit/autopsy into 7630-LargerReportWizard

This commit is contained in:
William Schaefer 2021-06-16 10:51:18 -04:00
commit 1497658729
70 changed files with 1883 additions and 930 deletions

View File

@ -130,8 +130,7 @@ public class FileManager implements Closeable {
* @throws TskCoreException * @throws TskCoreException
*/ */
public List<AbstractFile> findFilesExactName(long parentId, String name) throws TskCoreException{ public List<AbstractFile> findFilesExactName(long parentId, String name) throws TskCoreException{
String whereClause = "name = '%s'"; return caseDb.getFileManager().findFilesExactName(parentId, name);
return caseDb.findAllFilesInFolderWhere(parentId, String.format(whereClause, name));
} }
/** /**

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -119,9 +119,9 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
updateOutlineViewPanel(); updateOutlineViewPanel();
} }
}); });
TableColumn column = outline.getColumnModel().getColumn(2); TableColumn column = outline.getColumnModel().getColumn(2);
column.setCellRenderer(new NodeTableCellRenderer() ); column.setCellRenderer(new NodeTableCellRenderer());
} }
@ -164,7 +164,9 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
@Override @Override
public void setSelectionInfo(SelectionInfo info) { public void setSelectionInfo(SelectionInfo info) {
callLogDataViewer.setNode(null);
nodeFactory.refresh(info); nodeFactory.refresh(info);
} }
@Override @Override
@ -229,7 +231,7 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie
} }
} }
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane bottomScrollPane; private javax.swing.JScrollPane bottomScrollPane;
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel; private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel;

View File

@ -35,7 +35,8 @@ import org.sleuthkit.datamodel.TskCoreException;
/** /**
* ChildFactory for ContactNodes. * ChildFactory for ContactNodes.
*/ */
final class ContactsChildNodeFactory extends ChildFactory<BlackboardArtifact>{ final class ContactsChildNodeFactory extends ChildFactory<BlackboardArtifact> {
private static final Logger logger = Logger.getLogger(ContactsChildNodeFactory.class.getName()); private static final Logger logger = Logger.getLogger(ContactsChildNodeFactory.class.getName());
private SelectionInfo selectionInfo; private SelectionInfo selectionInfo;
@ -47,12 +48,13 @@ final class ContactsChildNodeFactory extends ChildFactory<BlackboardArtifact>{
* accounts * accounts
*/ */
ContactsChildNodeFactory(SelectionInfo selectionInfo) { ContactsChildNodeFactory(SelectionInfo selectionInfo) {
this.selectionInfo = selectionInfo; this.selectionInfo = selectionInfo;
} }
/** /**
* Updates the current instance of selectionInfo and calls the refresh method. * Updates the current instance of selectionInfo and calls the refresh
* * method.
*
* @param selectionInfo New instance of the currently selected accounts * @param selectionInfo New instance of the currently selected accounts
*/ */
public void refresh(SelectionInfo selectionInfo) { public void refresh(SelectionInfo selectionInfo) {
@ -63,13 +65,15 @@ final class ContactsChildNodeFactory extends ChildFactory<BlackboardArtifact>{
/** /**
* Creates a list of Keys (BlackboardArtifact) for only contacts of the * Creates a list of Keys (BlackboardArtifact) for only contacts of the
* currently selected accounts * currently selected accounts
*
* @param list List of BlackboardArtifact to populate * @param list List of BlackboardArtifact to populate
*
* @return True on success * @return True on success
*/ */
@Override @Override
protected boolean createKeys(List<BlackboardArtifact> list) { protected boolean createKeys(List<BlackboardArtifact> list) {
if(selectionInfo == null) { if (selectionInfo == null) {
return true; return true;
} }
@ -80,7 +84,7 @@ final class ContactsChildNodeFactory extends ChildFactory<BlackboardArtifact>{
logger.log(Level.SEVERE, "Failed to load relationship sources.", ex); //NON-NLS logger.log(Level.SEVERE, "Failed to load relationship sources.", ex); //NON-NLS
return false; return false;
} }
relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> { relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> {
BlackboardArtifact bba = (BlackboardArtifact) content; BlackboardArtifact bba = (BlackboardArtifact) content;

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -35,7 +35,6 @@ import org.openide.nodes.NodeAdapter;
import org.openide.nodes.NodeMemberEvent; import org.openide.nodes.NodeMemberEvent;
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.sleuthkit.autopsy.communications.ModifiableProxyLookup; import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
@ -126,6 +125,7 @@ final class ContactsViewer extends JPanel implements RelationshipsViewer {
@Override @Override
public void setSelectionInfo(SelectionInfo info) { public void setSelectionInfo(SelectionInfo info) {
contactPane.setNode(null);
nodeFactory.refresh(info); nodeFactory.refresh(info);
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -99,18 +99,19 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
public void setSelectionInfo(SelectionInfo info) { public void setSelectionInfo(SelectionInfo info) {
Set<Content> relationshipSources; Set<Content> relationshipSources;
Set<BlackboardArtifact> artifactList = new HashSet<>(); Set<BlackboardArtifact> artifactList = new HashSet<>();
contentViewer.setNode(null);
if (info != null) {
try {
relationshipSources = info.getRelationshipSources();
try { relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> {
relationshipSources = info.getRelationshipSources(); artifactList.add((BlackboardArtifact) content);
});
relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> { } catch (TskCoreException ex) {
artifactList.add((BlackboardArtifact) content); logger.log(Level.WARNING, "Unable to update selection.", ex);
}); }
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to update selection.", ex);
} }
thumbnailViewer.resetComponent(); thumbnailViewer.resetComponent();
thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(new AttachmentThumbnailsChildren(artifactList)), tableEM), true, this.getClass().getName())); thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(new AttachmentThumbnailsChildren(artifactList)), tableEM), true, this.getClass().getName()));

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,7 +18,6 @@
*/ */
package org.sleuthkit.autopsy.communications.relationships; package org.sleuthkit.autopsy.communications.relationships;
import java.awt.Component;
import javax.swing.JPanel; import javax.swing.JPanel;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup; import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
@ -37,15 +36,15 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider
*/ */
public RelationshipBrowser() { public RelationshipBrowser() {
initComponents(); initComponents();
MessageViewer messagesViewer = new MessageViewer(); MessageViewer messagesViewer = new MessageViewer();
ContactsViewer contactsViewer = new ContactsViewer(); ContactsViewer contactsViewer = new ContactsViewer();
SummaryViewer summaryViewer = new SummaryViewer(); SummaryViewer summaryViewer = new SummaryViewer();
MediaViewer mediaViewer = new MediaViewer(); MediaViewer mediaViewer = new MediaViewer();
CallLogViewer callLogViewer = new CallLogViewer(); CallLogViewer callLogViewer = new CallLogViewer();
proxyLookup = new ModifiableProxyLookup(messagesViewer.getLookup()); proxyLookup = new ModifiableProxyLookup(messagesViewer.getLookup());
tabPane.add(summaryViewer.getDisplayName(), summaryViewer); tabPane.add(summaryViewer.getDisplayName(), summaryViewer);
tabPane.add(messagesViewer.getDisplayName(), messagesViewer); tabPane.add(messagesViewer.getDisplayName(), messagesViewer);
tabPane.add(callLogViewer.getDisplayName(), callLogViewer); tabPane.add(callLogViewer.getDisplayName(), callLogViewer);
@ -95,13 +94,11 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void tabPaneStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_tabPaneStateChanged private void tabPaneStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_tabPaneStateChanged
if(currentSelection != null) { RelationshipsViewer viewer = ((RelationshipsViewer) tabPane.getSelectedComponent());
((RelationshipsViewer) tabPane.getSelectedComponent()).setSelectionInfo(currentSelection); //clear old values
} viewer.setSelectionInfo(currentSelection);
if (viewer instanceof Lookup.Provider) {
Component selectedComponent = tabPane.getSelectedComponent(); Lookup lookup = viewer.getLookup();
if(selectedComponent instanceof Lookup.Provider) {
Lookup lookup = ((Lookup.Provider)selectedComponent).getLookup();
proxyLookup.setNewLookups(lookup); proxyLookup.setNewLookups(lookup);
} }
}//GEN-LAST:event_tabPaneStateChanged }//GEN-LAST:event_tabPaneStateChanged

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018-2020 Basis Technology Corp. * Copyright 2018-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -21,10 +21,7 @@ package org.sleuthkit.autopsy.contentviewers;
import java.awt.Component; import java.awt.Component;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JLabel;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import javax.swing.text.EditorKit;
import javax.swing.text.html.HTMLEditorKit;
import static org.openide.util.NbBundle.Messages; import static org.openide.util.NbBundle.Messages;
import org.openide.nodes.Node; import org.openide.nodes.Node;
@ -36,6 +33,7 @@ import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.contentviewers.application.Annotations; import org.sleuthkit.autopsy.contentviewers.application.Annotations;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerHtmlStyles;
/** /**
* Annotations view of file contents. * Annotations view of file contents.
@ -48,33 +46,6 @@ import org.jsoup.nodes.Document;
"AnnotationsContentViewer.onEmpty=No annotations were found for this particular item." "AnnotationsContentViewer.onEmpty=No annotations were found for this particular item."
}) })
public class AnnotationsContentViewer extends javax.swing.JPanel implements DataContentViewer { public class AnnotationsContentViewer extends javax.swing.JPanel implements DataContentViewer {
private static final int DEFAULT_FONT_SIZE = new JLabel().getFont().getSize();
// how big the subheader should be
private static final int SUBHEADER_FONT_SIZE = DEFAULT_FONT_SIZE * 12 / 11;
// how big the header should be
private static final int HEADER_FONT_SIZE = DEFAULT_FONT_SIZE * 14 / 11;
// the subsection indent
private static final int DEFAULT_SUBSECTION_LEFT_PAD = DEFAULT_FONT_SIZE;
// spacing occurring after an item
private static final int DEFAULT_SECTION_SPACING = DEFAULT_FONT_SIZE * 2;
private static final int DEFAULT_SUBSECTION_SPACING = DEFAULT_FONT_SIZE / 2;
private static final int CELL_SPACING = DEFAULT_FONT_SIZE / 2;
// additional styling for components
private static final String STYLE_SHEET_RULE
= String.format(" .%s { font-size: %dpx;font-style:italic; margin: 0px; padding: 0px; } ", Annotations.MESSAGE_CLASSNAME, DEFAULT_FONT_SIZE)
+ String.format(" .%s {font-size:%dpx;font-weight:bold; margin: 0px; margin-top: %dpx; padding: 0px; } ",
Annotations.SUBHEADER_CLASSNAME, SUBHEADER_FONT_SIZE, DEFAULT_SUBSECTION_SPACING)
+ String.format(" .%s { font-size:%dpx;font-weight:bold; margin: 0px; padding: 0px; } ", Annotations.HEADER_CLASSNAME, HEADER_FONT_SIZE)
+ String.format(" td { vertical-align: top; font-size:%dpx; text-align: left; margin: 0px; padding: 0px %dpx 0px 0px;} ", DEFAULT_FONT_SIZE, CELL_SPACING)
+ String.format(" th { vertical-align: top; text-align: left; margin: 0px; padding: 0px %dpx 0px 0px} ", DEFAULT_FONT_SIZE, CELL_SPACING)
+ String.format(" .%s { margin: %dpx 0px; padding-left: %dpx; } ", Annotations.SUBSECTION_CLASSNAME, DEFAULT_SUBSECTION_SPACING, DEFAULT_SUBSECTION_LEFT_PAD)
+ String.format(" .%s { margin-bottom: %dpx; } ", Annotations.SECTION_CLASSNAME, DEFAULT_SECTION_SPACING);
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(AnnotationsContentViewer.class.getName()); private static final Logger logger = Logger.getLogger(AnnotationsContentViewer.class.getName());
@ -86,13 +57,7 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
*/ */
public AnnotationsContentViewer() { public AnnotationsContentViewer() {
initComponents(); initComponents();
Utilities.configureTextPaneAsHtml(textPanel); ContentViewerHtmlStyles.setupHtmlJTextPane(textPanel);
// get html editor kit and apply additional style rules
EditorKit editorKit = textPanel.getEditorKit();
if (editorKit instanceof HTMLEditorKit) {
HTMLEditorKit htmlKit = (HTMLEditorKit) editorKit;
htmlKit.getStyleSheet().addRule(STYLE_SHEET_RULE);
}
} }
@Override @Override
@ -223,7 +188,7 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
if(doc != null) { if(doc != null) {
return doc.html(); return doc.html();
} else { } else {
return Bundle.AnnotationsContentViewer_onEmpty(); return "<span class='" + ContentViewerHtmlStyles.getMessageClassName() + "'>" + Bundle.AnnotationsContentViewer_onEmpty() + "</span>";
} }
} }
@ -235,6 +200,7 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
try { try {
String text = get(); String text = get();
ContentViewerHtmlStyles.setStyles(textPanel);
textPanel.setText(text); textPanel.setText(text);
textPanel.setCaretPosition(0); textPanel.setCaretPosition(0);
} catch (InterruptedException | ExecutionException ex) { } catch (InterruptedException | ExecutionException ex) {

View File

@ -41,8 +41,6 @@
<SubComponents> <SubComponents>
<Container class="javax.swing.JScrollPane" name="jScrollPane2"> <Container class="javax.swing.JScrollPane" name="jScrollPane2">
<Properties> <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"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[610, 52]"/> <Dimension value="[610, 52]"/>
</Property> </Property>

View File

@ -20,16 +20,22 @@ package org.sleuthkit.autopsy.contentviewers;
import java.awt.Component; import java.awt.Component;
import java.awt.Cursor; import java.awt.Cursor;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
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 java.util.stream.Stream;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerHtmlStyles;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.coreutils.EscapeUtil;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
@ -63,6 +69,7 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
public Metadata() { public Metadata() {
initComponents(); initComponents();
customizeComponents(); customizeComponents();
ContentViewerHtmlStyles.setupHtmlJTextPane(jTextPane1);
} }
/** /**
@ -80,8 +87,6 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
setPreferredSize(new java.awt.Dimension(100, 52)); setPreferredSize(new java.awt.Dimension(100, 52));
jScrollPane2.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
jScrollPane2.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
jScrollPane2.setPreferredSize(new java.awt.Dimension(610, 52)); jScrollPane2.setPreferredSize(new java.awt.Dimension(610, 52));
jTextPane1.setEditable(false); jTextPane1.setEditable(false);
@ -116,30 +121,59 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
* selectAllMenuItem.addActionListener(actList); * selectAllMenuItem.addActionListener(actList);
*/ */
Utilities.configureTextPaneAsHtml(jTextPane1);
} }
private void setText(String str) { private void setText(String str) {
jTextPane1.setText("<html><body>" + str + "</body></html>"); //NON-NLS ContentViewerHtmlStyles.setupHtmlJTextPane(jTextPane1);
jTextPane1.setText("<html><head></head><body>" + str + "</body></html>"); //NON-NLS
}
private void addHeader(StringBuilder sb, String header, boolean spaced) {
sb.append(MessageFormat.format("<div class=\"{0}\"><h1 class=\"{1}\">{2}</h1></div>",
(spaced) ? ContentViewerHtmlStyles.getSpacedSectionClassName() : "",
ContentViewerHtmlStyles.getHeaderClassName(),
header));
} }
private void startTable(StringBuilder sb) { private void startTable(StringBuilder sb) {
sb.append("<table>"); //NON-NLS sb.append(MessageFormat.format("<table class=\"{0}\"><tbody>",
ContentViewerHtmlStyles.getIndentedClassName())); //NON-NLS
} }
private void endTable(StringBuilder sb) { private void endTable(StringBuilder sb) {
sb.append("</table>"); //NON-NLS sb.append("</tbody></table>"); //NON-NLS
} }
private void addRow(StringBuilder sb, String key, String value) { private void addRow(StringBuilder sb, String key, String value) {
sb.append("<tr><td valign=\"top\">"); //NON-NLS sb.append(MessageFormat.format("<tr><td class=\"{0}\"><span class=\"{1}\">{2}:</span></td><td class=\"{3}\">{4}</td></tr>",
sb.append(key); ContentViewerHtmlStyles.getKeyColumnClassName(),
sb.append("</td><td>"); //NON-NLS ContentViewerHtmlStyles.getTextClassName(),
sb.append(value); EscapeUtil.escapeHtml(key),
sb.append("</td></tr>"); //NON-NLS ContentViewerHtmlStyles.getTextClassName(),
EscapeUtil.escapeHtml(key)
));
}
private void addMonospacedRow(StringBuilder sb, String key) {
sb.append(MessageFormat.format("<tr><td class=\"{0}\"><span class=\"{1}\">{2}</span></td></tr>",
ContentViewerHtmlStyles.getKeyColumnClassName(),
ContentViewerHtmlStyles.getMonospacedClassName(),
EscapeUtil.escapeHtml(key)
));
}
private void addRowWithMultipleValues(StringBuilder sb, String key, String[] values) {
String[] safeValues = values == null || values.length < 1 ? new String[]{""} : values;
addRow(sb, key, safeValues[0]);
Stream.of(safeValues)
.skip(1)
.filter(line -> line != null)
.forEach(line -> addRow(sb, "", EscapeUtil.escapeHtml(line)));
} }
@Messages({ @Messages({
"Metadata.headerTitle=Metadata",
"Metadata.tableRowTitle.mimeType=MIME Type", "Metadata.tableRowTitle.mimeType=MIME Type",
"Metadata.nodeText.truncated=(results truncated)", "Metadata.nodeText.truncated=(results truncated)",
"Metadata.tableRowTitle.sha1=SHA1", "Metadata.tableRowTitle.sha1=SHA1",
@ -219,8 +253,11 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
if (StringUtils.isEmpty(details)) { if (StringUtils.isEmpty(details)) {
details = Bundle.Metadata_nodeText_unknown(); details = Bundle.Metadata_nodeText_unknown();
} }
details = details.replaceAll("\n", "<br>"); String[] lines = (details != null) ? details.split("\n") : new String[]{""};
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.acquisitionDetails"), details); addRowWithMultipleValues(sb,
NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.acquisitionDetails"),
lines);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error reading acquisition details from case database", ex); //NON-NLS LOGGER.log(Level.SEVERE, "Error reading acquisition details from case database", ex); //NON-NLS
} }
@ -240,11 +277,9 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
if (node != null && !node.getLookup().lookupAll(DataArtifact.class).isEmpty()) { if (node != null && !node.getLookup().lookupAll(DataArtifact.class).isEmpty()) {
return Bundle.Metadata_dataArtifactTitle(); return Bundle.Metadata_dataArtifactTitle();
} else { } else {
return NbBundle.getMessage(this.getClass(), "Metadata.title"); return NbBundle.getMessage(this.getClass(), "Metadata.title");
} }
} }
@Override @Override
public String getToolTip() { public String getToolTip() {
@ -299,6 +334,7 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
} }
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
addHeader(sb, Bundle.Metadata_headerTitle(), false);
startTable(sb); startTable(sb);
if (file != null) { if (file != null) {
@ -357,30 +393,35 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
* If we have a file system file, grab the more detailed * If we have a file system file, grab the more detailed
* metadata text too * metadata text too
*/ */
try { if (file instanceof FsContent) {
if (file instanceof FsContent) { FsContent fsFile = (FsContent) file;
FsContent fsFile = (FsContent) file;
sb.append("<hr /><pre>\n"); //NON-NLS addHeader(sb, NbBundle.getMessage(this.getClass(), "Metadata.nodeText.text"), true);
sb.append(NbBundle.getMessage(this.getClass(), "Metadata.nodeText.text")); startTable(sb);
sb.append(" <br /><br />"); // NON-NLS
for (String str : fsFile.getMetaDataText()) {
sb.append(str).append("<br />"); //NON-NLS
/* List<String> istatStrings = Collections.emptyList();
try {
istatStrings = fsFile.getMetaDataText();
} catch (TskCoreException ex) {
istatStrings = Arrays.asList(NbBundle.getMessage(this.getClass(), "Metadata.nodeText.exceptionNotice.text") + ex.getLocalizedMessage());
}
for (String str : istatStrings) {
addMonospacedRow(sb, str);
/*
* Very long results can cause the UI to hang before * Very long results can cause the UI to hang before
* displaying, so truncate the results if necessary. * displaying, so truncate the results if necessary.
*/ */
if (sb.length() > 50000) { if (sb.length() > 50000) {
sb.append(NbBundle.getMessage(this.getClass(), "Metadata.nodeText.truncated")); addMonospacedRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.nodeText.truncated"));
break; break;
}
} }
sb.append("</pre>\n"); //NON-NLS
} }
} catch (TskCoreException ex) {
sb.append(NbBundle.getMessage(this.getClass(), "Metadata.nodeText.exceptionNotice.text")).append(ex.getLocalizedMessage()); endTable(sb);
} }
} else { } else {
try { try {
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.name"), image.getUniquePath()); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.name"), image.getUniquePath());
@ -419,20 +460,18 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
// Add all the data source paths to the "Local Path" value cell. // Add all the data source paths to the "Local Path" value cell.
String[] imagePaths = image.getPaths(); String[] imagePaths = image.getPaths();
if (imagePaths.length > 0) { if (imagePaths.length > 0) {
StringBuilder pathValues = new StringBuilder("<div>"); addRowWithMultipleValues(sb,
pathValues.append(imagePaths[0]); NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.localPath"),
pathValues.append("</div>"); imagePaths);
for (int i = 1; i < imagePaths.length; i++) {
pathValues.append("<div>");
pathValues.append(imagePaths[i]);
pathValues.append("</div>");
}
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.localPath"), pathValues.toString());
} else { } else {
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.localPath"), addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.localPath"),
NbBundle.getMessage(this.getClass(), "Metadata.nodeText.none")); NbBundle.getMessage(this.getClass(), "Metadata.nodeText.none"));
} }
endTable(sb);
} }
if (isCancelled()) { if (isCancelled()) {

View File

@ -42,9 +42,6 @@
<Component class="javax.swing.JTextPane" name="textPanel"> <Component class="javax.swing.JTextPane" name="textPanel">
<Properties> <Properties>
<Property name="editable" type="boolean" value="false"/> <Property name="editable" type="boolean" value="false"/>
<Property name="background" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="DEFAULT_BACKGROUND" type="code"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/> <Property name="name" type="java.lang.String" value="" noResource="true"/>
<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="[600, 52]"/> <Dimension value="[600, 52]"/>

View File

@ -18,20 +18,21 @@
*/ */
package org.sleuthkit.autopsy.contentviewers.analysisresults; package org.sleuthkit.autopsy.contentviewers.analysisresults;
import java.awt.Color;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import javax.swing.JLabel;
import javax.swing.text.html.HTMLEditorKit;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.contentviewers.analysisresults.AnalysisResultsViewModel.NodeResults; import org.sleuthkit.autopsy.contentviewers.analysisresults.AnalysisResultsViewModel.NodeResults;
import org.sleuthkit.autopsy.contentviewers.analysisresults.AnalysisResultsViewModel.ResultDisplayAttributes; import org.sleuthkit.autopsy.contentviewers.analysisresults.AnalysisResultsViewModel.ResultDisplayAttributes;
import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerHtmlStyles;
import org.sleuthkit.autopsy.coreutils.EscapeUtil;
import org.sleuthkit.datamodel.AnalysisResult; import org.sleuthkit.datamodel.AnalysisResult;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.Score;
/** /**
@ -40,57 +41,19 @@ import org.sleuthkit.datamodel.Score;
public class AnalysisResultsContentPanel extends javax.swing.JPanel { public class AnalysisResultsContentPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String EMPTY_HTML = "<html><head></head><body></body></html>"; private static final String EMPTY_HTML = "<html><head></head><body></body></html>";
private static final String DEFAULT_FONT_FAMILY = new JLabel().getFont().getFamily();
private static final int DEFAULT_FONT_SIZE = new JLabel().getFont().getSize();
private static final Color DEFAULT_BACKGROUND = new JLabel().getBackground();
// html stylesheet classnames for components
private static final String ANALYSIS_RESULTS_CLASS_PREFIX = "analysisResult_";
private static final String SPACED_SECTION_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "spacedSection";
private static final String SUBSECTION_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "subsection";
private static final String HEADER_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "header";
public static final String MESSAGE_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "message";
public static final String TD_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "td";
// Anchors are inserted into the navigation so that the viewer can navigate to a selection. // Anchors are inserted into the navigation so that the viewer can navigate to a selection.
// This is the prefix of those anchors. // This is the prefix of those anchors.
private static final String RESULT_ANCHOR_PREFIX = "AnalysisResult_"; private static final String RESULT_ANCHOR_PREFIX = "AnalysisResult_";
// how big the header should be
private static final int HEADER_FONT_SIZE = DEFAULT_FONT_SIZE + 2;
// spacing occurring after an item
private static final int DEFAULT_SECTION_SPACING = DEFAULT_FONT_SIZE / 2;
private static final int CELL_SPACING = DEFAULT_FONT_SIZE / 2;
// the subsection indent
private static final int DEFAULT_SUBSECTION_LEFT_PAD = DEFAULT_FONT_SIZE;
// additional styling for components
private static final String STYLE_SHEET_RULE
= String.format(" .%s { font-size: %dpt;font-style:italic; margin: 0px; padding: 0px; } ", MESSAGE_CLASSNAME, DEFAULT_FONT_SIZE)
+ String.format(" .%s { font-family: %s; font-size: %dpt; font-weight: bold; margin: 0px; padding: 0px; } ",
HEADER_CLASSNAME, DEFAULT_FONT_FAMILY, HEADER_FONT_SIZE)
+ String.format(" .%s { vertical-align: top; font-family: %s; font-size: %dpt; text-align: left; margin: 0pt; padding: 0px %dpt 0px 0px;} ",
TD_CLASSNAME, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, CELL_SPACING)
+ String.format(" .%s { margin-top: %dpt; } ", SPACED_SECTION_CLASSNAME, DEFAULT_SECTION_SPACING)
+ String.format(" .%s { padding-left: %dpt; }", SUBSECTION_CLASSNAME, DEFAULT_SUBSECTION_LEFT_PAD);
/** /**
* Creates new form AnalysisResultsContentViewer * Creates new form AnalysisResultsContentViewer
*/ */
public AnalysisResultsContentPanel() { public AnalysisResultsContentPanel() {
initComponents(); initComponents();
ContentViewerHtmlStyles.setupHtmlJTextPane(textPanel);
textPanel.setContentType("text/html;charset=UTF-8"); //NON-NLS
HTMLEditorKit kit = new HTMLEditorKit();
textPanel.setEditorKit(kit);
kit.getStyleSheet().addRule(STYLE_SHEET_RULE);
} }
/** /**
@ -99,10 +62,11 @@ public class AnalysisResultsContentPanel extends javax.swing.JPanel {
* @param message The message to be displayed. * @param message The message to be displayed.
*/ */
void showMessage(String message) { void showMessage(String message) {
ContentViewerHtmlStyles.setStyles(textPanel);
textPanel.setText("<html><head></head><body>" textPanel.setText("<html><head></head><body>"
+ MessageFormat.format("<p class='{0}'>{1}</p>", + MessageFormat.format("<p class=\"{0}\">{1}</p>",
MESSAGE_CLASSNAME, ContentViewerHtmlStyles.getMessageClassName(),
message == null ? "" : message) message == null ? "" : EscapeUtil.escapeHtml(message))
+ "</body></html>"); + "</body></html>");
} }
@ -118,31 +82,26 @@ public class AnalysisResultsContentPanel extends javax.swing.JPanel {
* *
* @param nodeResults The analysis results data to display. * @param nodeResults The analysis results data to display.
*/ */
@NbBundle.Messages("AnalysisResultsContentPanel_aggregateScore_displayKey=Aggregate Score")
void displayResults(NodeResults nodeResults) { void displayResults(NodeResults nodeResults) {
Document document = Jsoup.parse(EMPTY_HTML); Document document = Jsoup.parse(EMPTY_HTML);
Element body = document.getElementsByTag("body").first(); Element body = document.getElementsByTag("body").first();
// if there is an aggregate score, append a section with the value Optional<Element> panelHeader = appendPanelHeader(body, nodeResults.getContent(), nodeResults.getAggregateScore());
Optional<Score> aggregateScore = nodeResults.getAggregateScore();
if (aggregateScore.isPresent()) {
appendSection(body,
MessageFormat.format("{0}: {1}",
Bundle.AnalysisResultsContentPanel_aggregateScore_displayKey(),
aggregateScore.get().getSignificance().getDisplayName()),
Optional.empty());
}
// for each analysis result item, display the data. // for each analysis result item, display the data.
List<ResultDisplayAttributes> displayAttributes = nodeResults.getAnalysisResults(); List<ResultDisplayAttributes> displayAttributes = nodeResults.getAnalysisResults();
for (int idx = 0; idx < displayAttributes.size(); idx++) { for (int idx = 0; idx < displayAttributes.size(); idx++) {
AnalysisResultsViewModel.ResultDisplayAttributes resultAttrs = displayAttributes.get(idx); AnalysisResultsViewModel.ResultDisplayAttributes resultAttrs = displayAttributes.get(idx);
appendResult(body, idx, resultAttrs); Element sectionDiv = appendResult(body, idx, resultAttrs);
if (idx > 0 || panelHeader.isPresent()) {
sectionDiv.attr("class", ContentViewerHtmlStyles.getSpacedSectionClassName());
}
} }
// set the body html // set the body html
ContentViewerHtmlStyles.setStyles(textPanel);
textPanel.setText(document.html()); textPanel.setText(document.html());
// if there is a selected result scroll to it // if there is a selected result scroll to it
Optional<AnalysisResult> selectedResult = nodeResults.getSelectedResult(); Optional<AnalysisResult> selectedResult = nodeResults.getSelectedResult();
if (selectedResult.isPresent()) { if (selectedResult.isPresent()) {
@ -153,34 +112,80 @@ public class AnalysisResultsContentPanel extends javax.swing.JPanel {
} }
} }
/**
* Appends the header to the panel.
*
* @param parent The parent html element.
* @param content The content whose name will be added if present.
* @param score The aggregate score whose significance will be added if
* present.
*
* @return The html element.
*/
@Messages({
"AnalysisResultsContentPanel_aggregateScore_displayKey=Aggregate Score",
"AnalysisResultsContentPanel_content_displayKey=Item"
})
private Optional<Element> appendPanelHeader(Element parent, Optional<Content> content, Optional<Score> score) {
if (!content.isPresent() || !score.isPresent()) {
return Optional.empty();
}
Element container = parent.appendElement("div");
// if there is content append the name
content.ifPresent((c) -> {
container.appendElement("p")
.attr("class", ContentViewerHtmlStyles.getTextClassName())
.text(MessageFormat.format("{0}: {1}",
Bundle.AnalysisResultsContentPanel_content_displayKey(),
c.getName()));
});
// if there is an aggregate score, append the value
score.ifPresent((s) -> {
container.appendElement("p")
.attr("class", ContentViewerHtmlStyles.getTextClassName())
.text(MessageFormat.format("{0}: {1}",
Bundle.AnalysisResultsContentPanel_aggregateScore_displayKey(),
s.getSignificance().getDisplayName()));
});
return Optional.ofNullable(container);
}
/** /**
* Returns the anchor id to use with the analysis result (based on the id). * Returns the anchor id to use with the analysis result (based on the id).
*
* @param analysisResult The analysis result. * @param analysisResult The analysis result.
*
* @return The anchor id. * @return The anchor id.
*/ */
private String getAnchor(AnalysisResult analysisResult) { private String getAnchor(AnalysisResult analysisResult) {
return RESULT_ANCHOR_PREFIX + analysisResult.getId(); return RESULT_ANCHOR_PREFIX + analysisResult.getId();
} }
/** /**
* Appends a result item to the parent element of an html document. * Appends a result item to the parent element of an html document.
*
* @param parent The parent element. * @param parent The parent element.
* @param index The index of the item in the list of all items. * @param index The index of the item in the list of all items.
* @param attrs The attributes of this item. * @param attrs The attributes of this item.
*
* @return The result div.
*/ */
@NbBundle.Messages({"# {0} - analysisResultsNumber", @NbBundle.Messages({"# {0} - analysisResultsNumber",
"AnalysisResultsContentPanel_result_headerKey=Analysis Result {0}" "AnalysisResultsContentPanel_result_headerKey=Analysis Result {0}"
}) })
private void appendResult(Element parent, int index, AnalysisResultsViewModel.ResultDisplayAttributes attrs) { private Element appendResult(Element parent, int index, AnalysisResultsViewModel.ResultDisplayAttributes attrs) {
// create a new section with appropriate header // create a new section with appropriate header
Element sectionDiv = appendSection(parent, Element sectionDiv = appendSection(parent,
Bundle.AnalysisResultsContentPanel_result_headerKey(index + 1), Bundle.AnalysisResultsContentPanel_result_headerKey(index + 1),
Optional.ofNullable(getAnchor(attrs.getAnalysisResult()))); Optional.ofNullable(getAnchor(attrs.getAnalysisResult())));
// create a table // create a table
Element table = sectionDiv.appendElement("table"); Element table = sectionDiv.appendElement("table");
table.attr("class", SUBSECTION_CLASSNAME); table.attr("class", ContentViewerHtmlStyles.getIndentedClassName());
Element tableBody = table.appendElement("tbody"); Element tableBody = table.appendElement("tbody");
@ -188,15 +193,20 @@ public class AnalysisResultsContentPanel extends javax.swing.JPanel {
for (Pair<String, String> keyVal : attrs.getAttributesToDisplay()) { for (Pair<String, String> keyVal : attrs.getAttributesToDisplay()) {
Element row = tableBody.appendElement("tr"); Element row = tableBody.appendElement("tr");
String keyString = keyVal.getKey() == null ? "" : keyVal.getKey() + ":"; String keyString = keyVal.getKey() == null ? "" : keyVal.getKey() + ":";
row.appendElement("td") Element keyTd = row.appendElement("td")
.attr("class", ContentViewerHtmlStyles.getTextClassName());
keyTd.appendElement("span")
.text(keyString) .text(keyString)
.attr("class", TD_CLASSNAME); .attr("class", ContentViewerHtmlStyles.getKeyColumnClassName());
String valueString = keyVal.getValue() == null ? "" : keyVal.getValue(); String valueString = keyVal.getValue() == null ? "" : keyVal.getValue();
row.appendElement("td") row.appendElement("td")
.text(valueString) .text(valueString)
.attr("class", TD_CLASSNAME); .attr("class", ContentViewerHtmlStyles.getTextClassName());
} }
return sectionDiv;
} }
/** /**
@ -210,21 +220,25 @@ public class AnalysisResultsContentPanel extends javax.swing.JPanel {
*/ */
private Element appendSection(Element parent, String headerText, Optional<String> anchorId) { private Element appendSection(Element parent, String headerText, Optional<String> anchorId) {
Element sectionDiv = parent.appendElement("div"); Element sectionDiv = parent.appendElement("div");
// append an anchor tag if there is one // append an anchor tag if there is one
Element anchorEl = null;
if (anchorId.isPresent()) { if (anchorId.isPresent()) {
Element anchorEl = sectionDiv.appendElement("a"); anchorEl = sectionDiv.appendElement("a");
anchorEl.attr("name", anchorId.get()); anchorEl.attr("name", anchorId.get());
anchorEl.attr("style", "padding: 0px; margin: 0px; display: inline-block;");
} }
// set the class for the section
sectionDiv.attr("class", SPACED_SECTION_CLASSNAME);
// append the header // append the header
Element header = sectionDiv.appendElement("h1"); Element header = null;
header = (anchorEl == null)
? sectionDiv.appendElement("h1")
: anchorEl.appendElement("h1");
header.text(headerText); header.text(headerText);
header.attr("class", HEADER_CLASSNAME); header.attr("class", ContentViewerHtmlStyles.getHeaderClassName());
header.attr("style", "display: inline-block");
// return the section element // return the section element
return sectionDiv; return sectionDiv;
} }
@ -244,7 +258,6 @@ public class AnalysisResultsContentPanel extends javax.swing.JPanel {
setPreferredSize(new java.awt.Dimension(100, 58)); setPreferredSize(new java.awt.Dimension(100, 58));
textPanel.setEditable(false); textPanel.setEditable(false);
textPanel.setBackground(DEFAULT_BACKGROUND);
textPanel.setName(""); // NOI18N textPanel.setName(""); // NOI18N
textPanel.setPreferredSize(new java.awt.Dimension(600, 52)); textPanel.setPreferredSize(new java.awt.Dimension(600, 52));
scrollPane.setViewportView(textPanel); scrollPane.setViewportView(textPanel);

View File

@ -94,6 +94,7 @@ public class AnalysisResultsViewModel {
private final List<ResultDisplayAttributes> analysisResults; private final List<ResultDisplayAttributes> analysisResults;
private final Optional<AnalysisResult> selectedResult; private final Optional<AnalysisResult> selectedResult;
private final Optional<Score> aggregateScore; private final Optional<Score> aggregateScore;
private final Optional<Content> content;
/** /**
* Constructor. * Constructor.
@ -102,11 +103,13 @@ public class AnalysisResultsViewModel {
* @param selectedResult The selected analysis result or empty if none * @param selectedResult The selected analysis result or empty if none
* selected. * selected.
* @param aggregateScore The aggregate score or empty if no score. * @param aggregateScore The aggregate score or empty if no score.
* @param content The content associated with these results.
*/ */
NodeResults(List<ResultDisplayAttributes> analysisResults, Optional<AnalysisResult> selectedResult, Optional<Score> aggregateScore) { NodeResults(List<ResultDisplayAttributes> analysisResults, Optional<AnalysisResult> selectedResult, Optional<Score> aggregateScore, Optional<Content> content) {
this.analysisResults = analysisResults; this.analysisResults = analysisResults;
this.selectedResult = selectedResult; this.selectedResult = selectedResult;
this.aggregateScore = aggregateScore; this.aggregateScore = aggregateScore;
this.content = content;
} }
/** /**
@ -135,6 +138,17 @@ public class AnalysisResultsViewModel {
Optional<Score> getAggregateScore() { Optional<Score> getAggregateScore() {
return aggregateScore; return aggregateScore;
} }
/**
* Returns the content associated with these results or empty if not
* present.
*
* @return The content associated with these results or empty if not
* present.
*/
Optional<Content> getContent() {
return content;
}
} }
/** /**
@ -221,10 +235,11 @@ public class AnalysisResultsViewModel {
*/ */
NodeResults getAnalysisResults(Node node) { NodeResults getAnalysisResults(Node node) {
if (node == null) { if (node == null) {
return new NodeResults(Collections.emptyList(), Optional.empty(), Optional.empty()); return new NodeResults(Collections.emptyList(), Optional.empty(), Optional.empty(), Optional.empty());
} }
Optional<Score> aggregateScore = Optional.empty(); Optional<Score> aggregateScore = Optional.empty();
Optional<Content> nodeContent = Optional.empty();
// maps id of analysis result to analysis result to prevent duplication // maps id of analysis result to analysis result to prevent duplication
Map<Long, AnalysisResult> allAnalysisResults = new HashMap<>(); Map<Long, AnalysisResult> allAnalysisResults = new HashMap<>();
Optional<AnalysisResult> selectedResult = Optional.empty(); Optional<AnalysisResult> selectedResult = Optional.empty();
@ -236,6 +251,8 @@ public class AnalysisResultsViewModel {
} }
try { try {
nodeContent = Optional.of(content);
// get the aggregate score of that content // get the aggregate score of that content
aggregateScore = Optional.ofNullable(content.getAggregateScore()); aggregateScore = Optional.ofNullable(content.getAggregateScore());
@ -273,6 +290,6 @@ public class AnalysisResultsViewModel {
// get view model representation // get view model representation
List<ResultDisplayAttributes> displayAttributes = getOrderedDisplayAttributes(allAnalysisResults.values()); List<ResultDisplayAttributes> displayAttributes = getOrderedDisplayAttributes(allAnalysisResults.values());
return new NodeResults(displayAttributes, selectedResult, aggregateScore); return new NodeResults(displayAttributes, selectedResult, aggregateScore, nodeContent);
} }
} }

View File

@ -24,7 +24,6 @@ import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.swing.JLabel;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
@ -39,6 +38,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil;
import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerHtmlStyles;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
@ -80,18 +80,6 @@ public class Annotations {
private static final String EMPTY_HTML = "<html><head></head><body></body></html>"; private static final String EMPTY_HTML = "<html><head></head><body></body></html>";
private static final int DEFAULT_FONT_SIZE = new JLabel().getFont().getSize();
// spacing occurring after an item
private static final int DEFAULT_TABLE_SPACING = DEFAULT_FONT_SIZE;
// html stylesheet classnames for components
public static final String MESSAGE_CLASSNAME = "message";
public static final String SUBSECTION_CLASSNAME = "subsection";
public static final String SUBHEADER_CLASSNAME = "subheader";
public static final String SECTION_CLASSNAME = "section";
public static final String HEADER_CLASSNAME = "header";
public static final String VERTICAL_TABLE_CLASSNAME = "vertical-table";
// describing table values for a tag // describing table values for a tag
private static final List<ItemEntry<Tag>> TAG_ENTRIES = Arrays.asList( private static final List<ItemEntry<Tag>> TAG_ENTRIES = Arrays.asList(
new ItemEntry<>(Bundle.Annotations_tagEntryDataLabel_tag(), new ItemEntry<>(Bundle.Annotations_tagEntryDataLabel_tag(),
@ -200,11 +188,11 @@ public class Annotations {
* @return If any content was actually rendered. * @return If any content was actually rendered.
*/ */
private static boolean renderArtifact(Element parent, BlackboardArtifact bba, Content sourceContent) { private static boolean renderArtifact(Element parent, BlackboardArtifact bba, Content sourceContent) {
boolean contentRendered = appendEntries(parent, TAG_CONFIG, getTags(bba), false); boolean contentRendered = appendEntries(parent, TAG_CONFIG, getTags(bba), false, true);
if (CentralRepository.isEnabled()) { if (CentralRepository.isEnabled()) {
List<CorrelationAttributeInstance> centralRepoComments = getCentralRepositoryData(bba); List<CorrelationAttributeInstance> centralRepoComments = getCentralRepositoryData(bba);
boolean crRendered = appendEntries(parent, CR_COMMENTS_CONFIG, centralRepoComments, false); boolean crRendered = appendEntries(parent, CR_COMMENTS_CONFIG, centralRepoComments, false, !contentRendered);
contentRendered = contentRendered || crRendered; contentRendered = contentRendered || crRendered;
} }
@ -213,12 +201,18 @@ public class Annotations {
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() == bba.getArtifactTypeID()) || BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() == bba.getArtifactTypeID())
&& (hasTskComment(bba))) { && (hasTskComment(bba))) {
boolean filesetRendered = appendEntries(parent, ARTIFACT_COMMENT_CONFIG, Arrays.asList(bba), false); boolean filesetRendered = appendEntries(parent, ARTIFACT_COMMENT_CONFIG, Arrays.asList(bba), false, !contentRendered);
contentRendered = contentRendered || filesetRendered; contentRendered = contentRendered || filesetRendered;
} }
Element sourceFileSection = appendSection(parent, Bundle.Annotations_sourceFile_title()); Element sourceFileSection = appendSection(parent, Bundle.Annotations_sourceFile_title());
boolean sourceFileRendered = renderContent(sourceFileSection, sourceContent, true); sourceFileSection.attr("class", ContentViewerHtmlStyles.getSpacedSectionClassName());
Element sourceFileContainer = sourceFileSection.appendElement("div");
sourceFileContainer.attr("class", ContentViewerHtmlStyles.getIndentedClassName());
boolean sourceFileRendered = renderContent(sourceFileContainer, sourceContent, true);
if (!sourceFileRendered) { if (!sourceFileRendered) {
sourceFileSection.remove(); sourceFileSection.remove();
@ -238,24 +232,27 @@ public class Annotations {
* @return If any content was actually rendered. * @return If any content was actually rendered.
*/ */
private static boolean renderContent(Element parent, Content sourceContent, boolean isSubheader) { private static boolean renderContent(Element parent, Content sourceContent, boolean isSubheader) {
boolean contentRendered = appendEntries(parent, TAG_CONFIG, getTags(sourceContent), isSubheader); boolean contentRendered = appendEntries(parent, TAG_CONFIG, getTags(sourceContent), isSubheader, true);
if (sourceContent instanceof AbstractFile) { if (sourceContent instanceof AbstractFile) {
AbstractFile sourceFile = (AbstractFile) sourceContent; AbstractFile sourceFile = (AbstractFile) sourceContent;
if (CentralRepository.isEnabled()) { if (CentralRepository.isEnabled()) {
List<CorrelationAttributeInstance> centralRepoComments = getCentralRepositoryData(sourceFile); List<CorrelationAttributeInstance> centralRepoComments = getCentralRepositoryData(sourceFile);
boolean crRendered = appendEntries(parent, CR_COMMENTS_CONFIG, centralRepoComments, isSubheader); boolean crRendered = appendEntries(parent, CR_COMMENTS_CONFIG, centralRepoComments, isSubheader,
!contentRendered);
contentRendered = contentRendered || crRendered; contentRendered = contentRendered || crRendered;
} }
boolean hashsetRendered = appendEntries(parent, HASHSET_CONFIG, boolean hashsetRendered = appendEntries(parent, HASHSET_CONFIG,
getFileSetHits(sourceFile, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT), getFileSetHits(sourceFile, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT),
isSubheader); isSubheader,
!contentRendered);
boolean interestingFileRendered = appendEntries(parent, INTERESTING_FILE_CONFIG, boolean interestingFileRendered = appendEntries(parent, INTERESTING_FILE_CONFIG,
getFileSetHits(sourceFile, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT), getFileSetHits(sourceFile, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT),
isSubheader); isSubheader,
!contentRendered);
contentRendered = contentRendered || hashsetRendered || interestingFileRendered; contentRendered = contentRendered || hashsetRendered || interestingFileRendered;
} }
@ -456,24 +453,36 @@ public class Annotations {
* will be formatted as a table in the format specified in the * will be formatted as a table in the format specified in the
* SectionConfig. * SectionConfig.
* *
* @param parent The parent element for which the entries will be * @param parent The parent element for which the entries will be
* attached. * attached.
* @param config The display configuration for this entry type (i.e. * @param config The display configuration for this entry type (i.e.
* table type, name, if data is not present). * table type, name, if data is not present).
* @param items The items to display. * @param items The items to display.
* @param isSubsection Whether or not this should be displayed as a * @param isSubsection Whether or not this should be displayed as a
* subsection. If not displayed as a top-level section. * subsection. If not displayed as a top-level
* section.
* @param isFirstSection Whether or not this is the first section appended.
* *
* @return If there was actual content rendered for this set of entries. * @return If there was actual content rendered for this set of entries.
*/ */
private static <T> boolean appendEntries(Element parent, Annotations.SectionConfig<T> config, List<? extends T> items, private static <T> boolean appendEntries(Element parent, Annotations.SectionConfig<T> config, List<? extends T> items,
boolean isSubsection) { boolean isSubsection, boolean isFirstSection) {
if (items == null || items.isEmpty()) { if (items == null || items.isEmpty()) {
return false; return false;
} }
Element sectionDiv = (isSubsection) ? appendSubsection(parent, config.getTitle()) : appendSection(parent, config.getTitle()); Element sectionDiv = (isSubsection) ? appendSubsection(parent, config.getTitle()) : appendSection(parent, config.getTitle());
appendVerticalEntryTables(sectionDiv, items, config.getAttributes()); if (!isFirstSection) {
sectionDiv.attr("class", ContentViewerHtmlStyles.getSpacedSectionClassName());
}
Element sectionContainer = sectionDiv.appendElement("div");
if (!isSubsection) {
sectionContainer.attr("class", ContentViewerHtmlStyles.getIndentedClassName());
}
appendVerticalEntryTables(sectionContainer, items, config.getAttributes());
return true; return true;
} }
@ -499,12 +508,11 @@ public class Annotations {
.collect(Collectors.toList()); .collect(Collectors.toList());
Element childTable = appendTable(parent, 2, tableData, null); Element childTable = appendTable(parent, 2, tableData, null);
childTable.attr("class", VERTICAL_TABLE_CLASSNAME);
if (isFirst) { if (isFirst) {
isFirst = false; isFirst = false;
} else { } else {
childTable.attr("style", String.format("margin-top: %dpx;", DEFAULT_TABLE_SPACING)); childTable.attr("class", ContentViewerHtmlStyles.getSpacedSectionClassName());
} }
} }
@ -551,6 +559,7 @@ public class Annotations {
Element row = rowParent.appendElement("tr"); Element row = rowParent.appendElement("tr");
for (int i = 0; i < columnNumber; i++) { for (int i = 0; i < columnNumber; i++) {
Element cell = row.appendElement(cellType); Element cell = row.appendElement(cellType);
cell.attr("class", ContentViewerHtmlStyles.getTextClassName());
if (data != null && i < data.size()) { if (data != null && i < data.size()) {
cell.text(StringUtils.isEmpty(data.get(i)) ? "" : data.get(i)); cell.text(StringUtils.isEmpty(data.get(i)) ? "" : data.get(i));
} }
@ -568,10 +577,9 @@ public class Annotations {
*/ */
private static Element appendSection(Element parent, String headerText) { private static Element appendSection(Element parent, String headerText) {
Element sectionDiv = parent.appendElement("div"); Element sectionDiv = parent.appendElement("div");
sectionDiv.attr("class", SECTION_CLASSNAME);
Element header = sectionDiv.appendElement("h1"); Element header = sectionDiv.appendElement("h1");
header.text(headerText); header.text(headerText);
header.attr("class", HEADER_CLASSNAME); header.attr("class", ContentViewerHtmlStyles.getHeaderClassName());
return sectionDiv; return sectionDiv;
} }
@ -585,10 +593,9 @@ public class Annotations {
*/ */
private static Element appendSubsection(Element parent, String headerText) { private static Element appendSubsection(Element parent, String headerText) {
Element subsectionDiv = parent.appendElement("div"); Element subsectionDiv = parent.appendElement("div");
subsectionDiv.attr("class", SUBSECTION_CLASSNAME);
Element header = subsectionDiv.appendElement("h2"); Element header = subsectionDiv.appendElement("h2");
header.text(headerText); header.text(headerText);
header.attr("class", SUBHEADER_CLASSNAME); header.attr("class", ContentViewerHtmlStyles.getHeaderClassName());
return subsectionDiv; return subsectionDiv;
} }
@ -605,7 +612,7 @@ public class Annotations {
private static Element appendMessage(Element parent, String message) { private static Element appendMessage(Element parent, String message) {
Element messageEl = parent.appendElement("p"); Element messageEl = parent.appendElement("p");
messageEl.text(message); messageEl.text(message);
messageEl.attr("class", MESSAGE_CLASSNAME); messageEl.attr("class", ContentViewerHtmlStyles.getMessageClassName());
return messageEl; return messageEl;
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Component; import java.awt.Component;
import java.awt.GridBagConstraints; import java.awt.GridBagConstraints;
import java.awt.GridBagLayout; import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -30,11 +31,13 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.border.EmptyBorder;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
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.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerDefaults;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.guiutils.ContactCache; import org.sleuthkit.autopsy.guiutils.ContactCache;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
@ -75,6 +78,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
*/ */
public CallLogArtifactViewer() { public CallLogArtifactViewer() {
initComponents(); initComponents();
this.setBorder(new EmptyBorder(ContentViewerDefaults.getPanelInsets()));
} }
/** /**
@ -93,29 +97,28 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
public void setArtifact(BlackboardArtifact artifact) { public void setArtifact(BlackboardArtifact artifact) {
resetComponent(); resetComponent();
if (artifact == null) {
return;
}
CallLogViewData callLogViewData = null; CallLogViewData callLogViewData = null;
try { try {
callLogViewData = getCallLogViewData(artifact); callLogViewData = getCallLogViewData(artifact);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting attributes for Calllog artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex); logger.log(Level.SEVERE, String.format("Error getting attributes for Calllog artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
} }
List<AccountPersonaSearcherData> personaSearchDataList = new ArrayList<>();
// update the view with the call log data // update the view with the call log data
if (callLogViewData != null) { if (callLogViewData != null) {
List<AccountPersonaSearcherData> personaSearchDataList = updateView(callLogViewData); personaSearchDataList.addAll(updateView(callLogViewData));
if (!personaSearchDataList.isEmpty()) {
currentAccountFetcher = new PersonaAccountFetcher(artifact, personaSearchDataList, this);
currentAccountFetcher.execute();
} else {
currentAccountFetcher = null;
}
} }
if (!personaSearchDataList.isEmpty()) {
currentAccountFetcher = new PersonaAccountFetcher(artifact, personaSearchDataList, this);
currentAccountFetcher.execute();
} else {
currentAccountFetcher = null;
}
// repaint // repaint
this.revalidate(); this.revalidate();
this.repaint();
} }
/** /**
@ -309,13 +312,13 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
}) })
private List<AccountPersonaSearcherData> updateView(CallLogViewData callLogViewData) { private List<AccountPersonaSearcherData> updateView(CallLogViewData callLogViewData) {
CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_parties()); CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, 0, Bundle.CallLogArtifactViewer_heading_parties());
List<AccountPersonaSearcherData> dataList = new ArrayList<>(); List<AccountPersonaSearcherData> dataList = new ArrayList<>();
// Display "From" if we have non-local device accounts // Display "From" if we have non-local device accounts
if (callLogViewData.getFromAccount() != null) { if (callLogViewData.getFromAccount() != null) {
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_from()); CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_from());
// check if this is local account // check if this is local account
String accountDisplayString = getAccountDisplayString(callLogViewData.getFromAccount(), callLogViewData); String accountDisplayString = getAccountDisplayString(callLogViewData.getFromAccount(), callLogViewData);
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString); CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
@ -366,8 +369,6 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
this.setLayout(m_gridBagLayout); this.setLayout(m_gridBagLayout);
this.revalidate(); this.revalidate();
this.repaint();
return dataList; return dataList;
} }
@ -384,7 +385,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
}) })
private void updateMetadataView(CallLogViewData callLogViewData) { private void updateMetadataView(CallLogViewData callLogViewData) {
CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_metadata()); CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, ContentViewerDefaults.getSectionSpacing(), Bundle.CallLogArtifactViewer_heading_metadata());
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_direction()); CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_direction());
if (callLogViewData.getDirection() != null) { if (callLogViewData.getDirection() != null) {
@ -414,7 +415,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
"CallLogArtifactViewer_heading_Source=Source", "CallLogArtifactViewer_heading_Source=Source",
"CallLogArtifactViewer_label_datasource=Data Source",}) "CallLogArtifactViewer_label_datasource=Data Source",})
private void updateSourceView(CallLogViewData callLogViewData) { private void updateSourceView(CallLogViewData callLogViewData) {
CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_Source()); CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, ContentViewerDefaults.getSectionSpacing(), Bundle.CallLogArtifactViewer_heading_Source());
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_datasource()); CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_datasource());
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDataSourceName()); CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDataSourceName());
} }
@ -432,7 +433,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
if (callLogViewData.getOtherAttributes().isEmpty()) { if (callLogViewData.getOtherAttributes().isEmpty()) {
return; return;
} }
CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_others()); CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, ContentViewerDefaults.getSectionSpacing(), Bundle.CallLogArtifactViewer_heading_others());
for (Map.Entry<String, String> entry : callLogViewData.getOtherAttributes().entrySet()) { for (Map.Entry<String, String> entry : callLogViewData.getOtherAttributes().entrySet()) {
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, entry.getKey()); CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, entry.getKey());
@ -444,9 +445,8 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
"CalllogArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas." "CalllogArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas."
}) })
private void showCRDisabledMessage() { private void showCRDisabledMessage() {
CommunicationArtifactViewerHelper.addBlankLine(this, m_gridBagLayout, m_constraints); Insets messageInsets = new Insets(ContentViewerDefaults.getSectionSpacing(), 0, ContentViewerDefaults.getLineSpacing(), 0);
m_constraints.gridy++; CommunicationArtifactViewerHelper.addMessageRow(this, m_gridBagLayout, messageInsets, m_constraints, Bundle.ContactArtifactViewer_cr_disabled_message());
CommunicationArtifactViewerHelper.addMessageRow(this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_cr_disabled_message());
m_constraints.gridy++; m_constraints.gridy++;
} }
@ -505,7 +505,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
m_constraints.gridx = 0; m_constraints.gridx = 0;
m_constraints.weighty = 0.0; m_constraints.weighty = 0.0;
m_constraints.weightx = 0.0; // keep components fixed horizontally. m_constraints.weightx = 0.0; // keep components fixed horizontally.
m_constraints.insets = new java.awt.Insets(0, CommunicationArtifactViewerHelper.LEFT_INSET, 0, 0); m_constraints.insets = new java.awt.Insets(0, ContentViewerDefaults.getSectionIndent(), 0, 0);
m_constraints.fill = GridBagConstraints.NONE; m_constraints.fill = GridBagConstraints.NONE;
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,6 @@
package org.sleuthkit.autopsy.contentviewers.artifactviewers; package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints; import java.awt.GridBagConstraints;
import java.awt.GridBagLayout; import java.awt.GridBagLayout;
import java.awt.Insets; import java.awt.Insets;
@ -38,6 +37,7 @@ import javax.swing.JTextPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerDefaults;
/** /**
* *
@ -49,8 +49,6 @@ final class CommunicationArtifactViewerHelper {
// Number of columns in the gridbag layout. // Number of columns in the gridbag layout.
private final static int MAX_COLS = 4; private final static int MAX_COLS = 4;
final static int LEFT_INSET = 12;
/** /**
* Empty private constructor * Empty private constructor
*/ */
@ -64,34 +62,34 @@ final class CommunicationArtifactViewerHelper {
* @param panel Panel to update. * @param panel Panel to update.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constraints to use. * @param constraints Constraints to use.
* @param spacing Spacing to add to top insets (in pixels).
* @param headerString Heading string to display. * @param headerString Heading string to display.
* *
* @return JLabel Heading label added. * @return JLabel Heading label added.
*/ */
static JLabel addHeader(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String headerString) { static JLabel addHeader(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, int topSpacing, String headerString) {
Insets savedInsets = constraints.insets; Insets savedInsets = constraints.insets;
// create label for heading // create label for heading
javax.swing.JLabel headingLabel = new javax.swing.JLabel(); javax.swing.JLabel headingLabel = new javax.swing.JLabel();
// add a blank line before the start of new section, unless it's
// the first section
if (constraints.gridy != 0) {
addBlankLine(panel, gridbagLayout, constraints);
}
constraints.gridy++; constraints.gridy++;
constraints.gridx = 0; constraints.gridx = 0;
// let the header span all of the row // let the header span all of the row
constraints.gridwidth = MAX_COLS; constraints.gridwidth = MAX_COLS;
constraints.insets = new Insets(0, 0, 0, 0); // No inset for header constraints.anchor = GridBagConstraints.LINE_START;
constraints.fill = GridBagConstraints.NONE;
constraints.insets = new Insets(topSpacing, 0, ContentViewerDefaults.getLineSpacing(), 0);
// set text // set text
headingLabel.setText(headerString); headingLabel.setText(headerString.trim());
// make it large and bold // make it large and bold
headingLabel.setFont(headingLabel.getFont().deriveFont(Font.BOLD, headingLabel.getFont().getSize() + 2)); headingLabel.setFont(ContentViewerDefaults.getHeaderFont());
// add to panel // add to panel
gridbagLayout.setConstraints(headingLabel, constraints); gridbagLayout.setConstraints(headingLabel, constraints);
@ -159,7 +157,7 @@ final class CommunicationArtifactViewerHelper {
int savedFill = constraints.fill; int savedFill = constraints.fill;
constraints.weightx = 1.0; // take up all the horizontal space constraints.weightx = 1.0; // take up all the horizontal space
constraints.fill = GridBagConstraints.BOTH; constraints.fill = GridBagConstraints.HORIZONTAL;
javax.swing.Box.Filler horizontalFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(32767, 0)); javax.swing.Box.Filler horizontalFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(32767, 0));
gridbagLayout.setConstraints(horizontalFiller, constraints); gridbagLayout.setConstraints(horizontalFiller, constraints);
@ -181,6 +179,7 @@ final class CommunicationArtifactViewerHelper {
static void addPageEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) { static void addPageEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
constraints.gridx = 0; constraints.gridx = 0;
constraints.gridy++;
double savedWeighty = constraints.weighty; double savedWeighty = constraints.weighty;
int savedFill = constraints.fill; int savedFill = constraints.fill;
@ -197,24 +196,6 @@ final class CommunicationArtifactViewerHelper {
constraints.fill = savedFill; constraints.fill = savedFill;
} }
/**
* Adds a blank line to the panel.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constraints to use.
*/
static void addBlankLine(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
constraints.gridy++;
constraints.gridx = 0;
javax.swing.JLabel filler = new javax.swing.JLabel(" ");
gridbagLayout.setConstraints(filler, constraints);
panel.add(filler);
addLineEndGlue(panel, gridbagLayout, constraints);
}
/** /**
* Adds a label/key to the panel at col 0. * Adds a label/key to the panel at col 0.
* *
@ -247,9 +228,12 @@ final class CommunicationArtifactViewerHelper {
constraints.gridy++; constraints.gridy++;
constraints.gridx = gridx < MAX_COLS - 1 ? gridx : MAX_COLS - 2; constraints.gridx = gridx < MAX_COLS - 1 ? gridx : MAX_COLS - 2;
constraints.anchor = GridBagConstraints.LINE_START;
constraints.insets = new Insets(0, ContentViewerDefaults.getSectionIndent(), ContentViewerDefaults.getLineSpacing(), 0);
// set text // set text
keyLabel.setText(keyString + ": "); String preppedKeyString = keyString == null ? null : keyString.trim() + ":";
keyLabel.setText(preppedKeyString);
// add to panel // add to panel
gridbagLayout.setConstraints(keyLabel, constraints); gridbagLayout.setConstraints(keyLabel, constraints);
@ -288,6 +272,7 @@ final class CommunicationArtifactViewerHelper {
JTextPane valueField = new JTextPane(); JTextPane valueField = new JTextPane();
valueField.setEditable(false); valueField.setEditable(false);
valueField.setOpaque(false); valueField.setOpaque(false);
valueField.setMargin(new Insets(0,0,0,0));
constraints.gridx = gridx < MAX_COLS ? gridx : MAX_COLS - 1; constraints.gridx = gridx < MAX_COLS ? gridx : MAX_COLS - 1;
@ -295,7 +280,8 @@ final class CommunicationArtifactViewerHelper {
// let the value span 2 cols // let the value span 2 cols
cloneConstraints.gridwidth = 2; cloneConstraints.gridwidth = 2;
cloneConstraints.fill = GridBagConstraints.BOTH; constraints.anchor = GridBagConstraints.LINE_START;
cloneConstraints.insets = new Insets(0, ContentViewerDefaults.getColumnSpacing(), ContentViewerDefaults.getLineSpacing(), 0);
// set text // set text
valueField.setText(valueString); valueField.setText(valueString);
@ -325,13 +311,13 @@ final class CommunicationArtifactViewerHelper {
* @param panel Panel to show. * @param panel Panel to show.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constraints to use. * @param constraints Constraints to use.
* * @param insets The insets to be used for the grid bag layout constraints. If null, default insets are assumed.
* @param messageString Message to display. * @param messageString Message to display.
* *
* @return Label for message added. * @return Label for message added.
*/ */
static JLabel addMessageRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String messageString) { static JLabel addMessageRow(JPanel panel, GridBagLayout gridbagLayout, Insets insets, GridBagConstraints constraints, String messageString) {
return addMessageRow(panel, gridbagLayout, constraints, messageString, 0); return addMessageRow(panel, gridbagLayout, constraints, insets, messageString, 0);
} }
/** /**
@ -340,26 +326,33 @@ final class CommunicationArtifactViewerHelper {
* *
* @param panel Panel to show. * @param panel Panel to show.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param insets The insets to be used for the grid bag layout constraints.
* @param constraints Constraints to use. * @param constraints Constraints to use.
* * @param insets The insets to be used for the grid bag layout constraints. If null, default insets are assumed.
* @param messageString Message to display. * @param messageString Message to display.
* @param gridx The grid x location to use.
* *
* @return Label for message added. * @return Label for message added.
*/ */
static JLabel addMessageRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String messageString, int gridx) { static JLabel addMessageRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, Insets insets, String messageString, int gridx) {
// create label // create label
javax.swing.JLabel messageLabel = new javax.swing.JLabel(); javax.swing.JLabel messageLabel = new javax.swing.JLabel();
constraints.gridy++; constraints.gridy++;
constraints.gridx = gridx < MAX_COLS - 1 ? gridx : MAX_COLS - 2; constraints.gridx = gridx < MAX_COLS - 1 ? gridx : MAX_COLS - 2;
constraints.insets = insets == null
? new Insets(0, 0, ContentViewerDefaults.getLineSpacing(), 0) :
insets;
constraints.anchor = GridBagConstraints.LINE_START;
int savedGridwidth = constraints.gridwidth; int savedGridwidth = constraints.gridwidth;
constraints.gridwidth = 3; constraints.gridwidth = 3;
// set text // set text
messageLabel.setText(messageString); messageLabel.setText(messageString == null ? null : messageString.trim());
messageLabel.setFont(ContentViewerDefaults.getMessageFont());
// add to panel // add to panel
gridbagLayout.setConstraints(messageLabel, constraints); gridbagLayout.setConstraints(messageLabel, constraints);
@ -406,8 +399,9 @@ final class CommunicationArtifactViewerHelper {
Insets savedInsets = constraints.insets; Insets savedInsets = constraints.insets;
// extra Indent in // extra Indent in
constraints.insets = new java.awt.Insets(0, 2 * LEFT_INSET, 0, 0); constraints.insets = new java.awt.Insets(0, ContentViewerDefaults.getColumnSpacing(), ContentViewerDefaults.getLineSpacing(), 0);
constraints.anchor = GridBagConstraints.LINE_START;
// create label // create label
javax.swing.JLabel personaLabel = new javax.swing.JLabel(); javax.swing.JLabel personaLabel = new javax.swing.JLabel();
String personaLabelText = Bundle.CommunicationArtifactViewerHelper_persona_label(); String personaLabelText = Bundle.CommunicationArtifactViewerHelper_persona_label();
@ -415,15 +409,12 @@ final class CommunicationArtifactViewerHelper {
? Bundle.CommunicationArtifactViewerHelper_persona_searching() ? Bundle.CommunicationArtifactViewerHelper_persona_searching()
: Bundle.CommunicationArtifactViewerHelper_persona_unknown()); : Bundle.CommunicationArtifactViewerHelper_persona_unknown());
personaLabel.setText(personaLabelText); personaLabel.setText(personaLabelText == null ? null : personaLabelText.trim());
// add to panel // add to panel
gridbagLayout.setConstraints(personaLabel, constraints); gridbagLayout.setConstraints(personaLabel, constraints);
panel.add(personaLabel); panel.add(personaLabel);
// restore constraint
constraints.insets = savedInsets;
constraints.gridx++; constraints.gridx++;
// Place a button as place holder. It will be enabled when persona is available. // Place a button as place holder. It will be enabled when persona is available.
@ -441,6 +432,9 @@ final class CommunicationArtifactViewerHelper {
} else { } else {
personaLabel.setEnabled(false); personaLabel.setEnabled(false);
} }
// restore constraint
constraints.insets = savedInsets;
addLineEndGlue(panel, gridbagLayout, constraints); addLineEndGlue(panel, gridbagLayout, constraints);
@ -469,7 +463,7 @@ final class CommunicationArtifactViewerHelper {
GridBagConstraints indentedConstraints = (GridBagConstraints) constraints.clone(); GridBagConstraints indentedConstraints = (GridBagConstraints) constraints.clone();
// Add an indent to match persona labels // Add an indent to match persona labels
indentedConstraints.insets = new java.awt.Insets(0, 2 * LEFT_INSET, 0, 0); indentedConstraints.insets = new java.awt.Insets(0, ContentViewerDefaults.getSectionIndent(), ContentViewerDefaults.getLineSpacing(), 0);
String contactInfo = Bundle.CommunicationArtifactViewerHelper_contact_label(contactId != null && !contactId.isEmpty() ? contactId : Bundle.CommunicationArtifactViewerHelper_contact_label_unknown()); String contactInfo = Bundle.CommunicationArtifactViewerHelper_contact_label(contactId != null && !contactId.isEmpty() ? contactId : Bundle.CommunicationArtifactViewerHelper_contact_label_unknown());

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -41,9 +41,9 @@ import javax.imageio.ImageIO;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
@ -57,6 +57,7 @@ import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallback; import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallback;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode; import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel; import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel;
import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerDefaults;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
@ -107,7 +108,7 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
*/ */
public ContactArtifactViewer() { public ContactArtifactViewer() {
initComponents(); initComponents();
this.setBorder(new EmptyBorder(ContentViewerDefaults.getPanelInsets()));
defaultImage = new ImageIcon(ContactArtifactViewer.class.getResource(DEFAULT_IMAGE_PATH)); defaultImage = new ImageIcon(ContactArtifactViewer.class.getResource(DEFAULT_IMAGE_PATH));
} }
@ -129,19 +130,15 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
// Reset the panel. // Reset the panel.
resetComponent(); resetComponent();
if (artifact == null) { if (artifact != null) {
return; try {
extractArtifactData(artifact);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting attributes for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
return;
}
updateView();
} }
try {
extractArtifactData(artifact);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting attributes for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
return;
}
updateView();
this.setLayout(this.m_gridBagLayout); this.setLayout(this.m_gridBagLayout);
this.revalidate(); this.revalidate();
this.repaint(); this.repaint();
@ -163,6 +160,7 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
* Extracts data from the artifact to be displayed in the panel. * Extracts data from the artifact to be displayed in the panel.
* *
* @param artifact Artifact to show. * @param artifact Artifact to show.
*
* @throws TskCoreException * @throws TskCoreException
*/ */
private void extractArtifactData(BlackboardArtifact artifact) throws TskCoreException { private void extractArtifactData(BlackboardArtifact artifact) throws TskCoreException {
@ -234,7 +232,7 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
/** /**
* Updates the contact image in the view. * Updates the contact image in the view.
* *
* @param contactPanelLayout Panel layout. * @param contactPanelLayout Panel layout.
* @param contactPanelConstraints Layout constraints. * @param contactPanelConstraints Layout constraints.
* *
*/ */
@ -245,8 +243,11 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
Insets savedInsets = contactPanelConstraints.insets; Insets savedInsets = contactPanelConstraints.insets;
contactPanelConstraints.gridy = 0; contactPanelConstraints.gridy = 0;
contactPanelConstraints.gridx = 0; contactPanelConstraints.gridx = 0;
contactPanelConstraints.insets = new Insets(0, 0, 0, 0); contactPanelConstraints.insets = new Insets(0, 0, ContentViewerDefaults.getLineSpacing(), 0);
int prevGridWidth = contactPanelConstraints.gridwidth;
contactPanelConstraints.gridwidth = 3;
contactPanelConstraints.anchor = GridBagConstraints.LINE_START;
javax.swing.JLabel contactImage = new javax.swing.JLabel(); javax.swing.JLabel contactImage = new javax.swing.JLabel();
contactImage.setIcon(getImageFromArtifact(contactArtifact)); contactImage.setIcon(getImageFromArtifact(contactArtifact));
contactImage.setText(Bundle.ContactArtifactViewer_contactImage_text()); contactImage.setText(Bundle.ContactArtifactViewer_contactImage_text());
@ -256,13 +257,14 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
CommunicationArtifactViewerHelper.addLineEndGlue(this, contactPanelLayout, contactPanelConstraints); CommunicationArtifactViewerHelper.addLineEndGlue(this, contactPanelLayout, contactPanelConstraints);
contactPanelConstraints.gridy++; contactPanelConstraints.gridy++;
contactPanelConstraints.gridwidth = prevGridWidth;
contactPanelConstraints.insets = savedInsets; contactPanelConstraints.insets = savedInsets;
} }
/** /**
* Updates the contact name in the view. * Updates the contact name in the view.
* *
* @param contactPanelLayout Panel layout. * @param contactPanelLayout Panel layout.
* @param contactPanelConstraints Layout constraints. * @param contactPanelConstraints Layout constraints.
* *
*/ */
@ -275,13 +277,13 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
if (StringUtils.isEmpty(bba.getValueString()) == false) { if (StringUtils.isEmpty(bba.getValueString()) == false) {
contactName = bba.getDisplayString(); contactName = bba.getDisplayString();
CommunicationArtifactViewerHelper.addHeader(this, contactPanelLayout, contactPanelConstraints, contactName); CommunicationArtifactViewerHelper.addHeader(this, contactPanelLayout, contactPanelConstraints, 0, contactName);
foundName = true; foundName = true;
break; break;
} }
} }
if (foundName == false) { if (foundName == false) {
CommunicationArtifactViewerHelper.addHeader(this, contactPanelLayout, contactPanelConstraints, Bundle.ContactArtifactViewer_contactname_unknown()); CommunicationArtifactViewerHelper.addHeader(this, contactPanelLayout, contactPanelConstraints, ContentViewerDefaults.getSectionSpacing(), Bundle.ContactArtifactViewer_contactname_unknown());
} }
} }
@ -289,9 +291,9 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
* Updates the view by displaying the given list of attributes in the given * Updates the view by displaying the given list of attributes in the given
* section panel. * section panel.
* *
* @param sectionAttributesList List of attributes to display. * @param sectionAttributesList List of attributes to display.
* @param sectionHeader Section name label. * @param sectionHeader Section name label.
* @param contactPanelLayout Panel layout. * @param contactPanelLayout Panel layout.
* @param contactPanelConstraints Layout constraints. * @param contactPanelConstraints Layout constraints.
* *
*/ */
@ -302,7 +304,7 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
return; return;
} }
CommunicationArtifactViewerHelper.addHeader(this, contactPanelLayout, contactPanelConstraints, sectionHeader); CommunicationArtifactViewerHelper.addHeader(this, contactPanelLayout, contactPanelConstraints, ContentViewerDefaults.getSectionSpacing(), sectionHeader);
for (BlackboardAttribute bba : sectionAttributesList) { for (BlackboardAttribute bba : sectionAttributesList) {
CommunicationArtifactViewerHelper.addKey(this, contactPanelLayout, contactPanelConstraints, bba.getAttributeType().getDisplayName()); CommunicationArtifactViewerHelper.addKey(this, contactPanelLayout, contactPanelConstraints, bba.getAttributeType().getDisplayName());
CommunicationArtifactViewerHelper.addValue(this, contactPanelLayout, contactPanelConstraints, bba.getDisplayString()); CommunicationArtifactViewerHelper.addValue(this, contactPanelLayout, contactPanelConstraints, bba.getDisplayString());
@ -316,7 +318,7 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
"ContactArtifactViewer_heading_Source=Source", "ContactArtifactViewer_heading_Source=Source",
"ContactArtifactViewer_label_datasource=Data Source",}) "ContactArtifactViewer_label_datasource=Data Source",})
private void updateSource() { private void updateSource() {
CommunicationArtifactViewerHelper.addHeader(this, this.m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_heading_Source()); CommunicationArtifactViewerHelper.addHeader(this, this.m_gridBagLayout, m_constraints, ContentViewerDefaults.getSectionSpacing(), Bundle.ContactArtifactViewer_heading_Source());
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_label_datasource()); CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_label_datasource());
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, m_constraints, datasourceName); CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, m_constraints, datasourceName);
} }
@ -335,7 +337,7 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
private void initiatePersonasSearch() { private void initiatePersonasSearch() {
// add a section header // add a section header
JLabel personaHeader = CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_persona_header()); JLabel personaHeader = CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, m_constraints, ContentViewerDefaults.getSectionSpacing(), Bundle.ContactArtifactViewer_persona_header());
m_constraints.gridy++; m_constraints.gridy++;
@ -346,8 +348,10 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
this.personaSearchStatusLabel = new javax.swing.JLabel(); this.personaSearchStatusLabel = new javax.swing.JLabel();
personaSearchStatusLabel.setText(personaStatusLabelText); personaSearchStatusLabel.setText(personaStatusLabelText);
personaSearchStatusLabel.setFont(ContentViewerDefaults.getMessageFont());
m_constraints.gridx = 0; m_constraints.gridx = 0;
m_constraints.anchor = GridBagConstraints.LINE_START;
CommunicationArtifactViewerHelper.addComponent(this, m_gridBagLayout, m_constraints, personaSearchStatusLabel); CommunicationArtifactViewerHelper.addComponent(this, m_gridBagLayout, m_constraints, personaSearchStatusLabel);
@ -359,9 +363,8 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
personaHeader.setEnabled(false); personaHeader.setEnabled(false);
personaSearchStatusLabel.setEnabled(false); personaSearchStatusLabel.setEnabled(false);
CommunicationArtifactViewerHelper.addBlankLine(this, m_gridBagLayout, m_constraints); Insets messageInsets = new Insets(ContentViewerDefaults.getSectionSpacing(), 0, ContentViewerDefaults.getLineSpacing(), 0);
m_constraints.gridy++; CommunicationArtifactViewerHelper.addMessageRow(this, m_gridBagLayout, messageInsets, m_constraints, Bundle.ContactArtifactViewer_cr_disabled_message());
CommunicationArtifactViewerHelper.addMessageRow(this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_cr_disabled_message());
m_constraints.gridy++; m_constraints.gridy++;
CommunicationArtifactViewerHelper.addPageEndGlue(this, m_gridBagLayout, this.m_constraints); CommunicationArtifactViewerHelper.addPageEndGlue(this, m_gridBagLayout, this.m_constraints);
@ -412,12 +415,12 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
/** /**
* Displays the given persona in the persona panel. * Displays the given persona in the persona panel.
* *
* @param persona Persona to display. * @param persona Persona to display.
* @param matchNumber Number of matches. * @param matchNumber Number of matches.
* @param missingAccountsList List of contact accounts this persona may be * @param missingAccountsList List of contact accounts this persona may be
* missing. * missing.
* @param gridBagLayout Layout to use. * @param gridBagLayout Layout to use.
* @param constraints layout constraints. * @param constraints layout constraints.
* *
* @throws CentralRepoException * @throws CentralRepoException
*/ */
@ -435,12 +438,9 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
// save the original insets // save the original insets
Insets savedInsets = constraints.insets; Insets savedInsets = constraints.insets;
// some label are indented 2x to appear indented w.r.t column above
Insets extraIndentInsets = new java.awt.Insets(0, 2 * CommunicationArtifactViewerHelper.LEFT_INSET, 0, 0);
// Add a Match X label in col 0. // Add a Match X label in col 0.
constraints.gridx = 0; constraints.gridx = 0;
javax.swing.JLabel matchNumberLabel = CommunicationArtifactViewerHelper.addKey(this, gridBagLayout, constraints, String.format("%s %d", Bundle.ContactArtifactViewer_persona_match_num(), matchNumber)); javax.swing.JLabel matchNumberLabel = CommunicationArtifactViewerHelper.addKey(this, gridBagLayout, constraints, String.format("%s %d", Bundle.ContactArtifactViewer_persona_match_num(), matchNumber).trim());
javax.swing.JLabel personaNameLabel = new javax.swing.JLabel(); javax.swing.JLabel personaNameLabel = new javax.swing.JLabel();
javax.swing.JButton personaButton = new javax.swing.JButton(); javax.swing.JButton personaButton = new javax.swing.JButton();
@ -461,6 +461,8 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
//constraints.gridwidth = 1; // TBD: this may not be needed if we use single panel //constraints.gridwidth = 1; // TBD: this may not be needed if we use single panel
constraints.gridx++; constraints.gridx++;
constraints.insets = new Insets(0, ContentViewerDefaults.getColumnSpacing(), ContentViewerDefaults.getLineSpacing(), 0);
constraints.anchor = GridBagConstraints.LINE_START;
personaNameLabel.setText(personaName); personaNameLabel.setText(personaName);
gridBagLayout.setConstraints(personaNameLabel, constraints); gridBagLayout.setConstraints(personaNameLabel, constraints);
CommunicationArtifactViewerHelper.addComponent(this, gridBagLayout, constraints, personaNameLabel); CommunicationArtifactViewerHelper.addComponent(this, gridBagLayout, constraints, personaNameLabel);
@ -474,6 +476,8 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
// Shirnk the button height. // Shirnk the button height.
personaButton.setMargin(new Insets(0, 5, 0, 5)); personaButton.setMargin(new Insets(0, 5, 0, 5));
constraints.insets = new Insets(0, ContentViewerDefaults.getColumnSpacing(), ContentViewerDefaults.getLineSpacing(), 0);
constraints.anchor = GridBagConstraints.LINE_START;
gridBagLayout.setConstraints(personaButton, constraints); gridBagLayout.setConstraints(personaButton, constraints);
CommunicationArtifactViewerHelper.addComponent(this, gridBagLayout, constraints, personaButton); CommunicationArtifactViewerHelper.addComponent(this, gridBagLayout, constraints, personaButton);
CommunicationArtifactViewerHelper.addLineEndGlue(this, gridBagLayout, constraints); CommunicationArtifactViewerHelper.addLineEndGlue(this, gridBagLayout, constraints);
@ -488,7 +492,8 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
//constraints.insets = labelInsets; //constraints.insets = labelInsets;
javax.swing.JLabel accountsStatus = new javax.swing.JLabel(Bundle.ContactArtifactViewer_found_all_accounts_label()); javax.swing.JLabel accountsStatus = new javax.swing.JLabel(Bundle.ContactArtifactViewer_found_all_accounts_label());
constraints.insets = extraIndentInsets; constraints.insets = new Insets(0, ContentViewerDefaults.getColumnSpacing(), ContentViewerDefaults.getLineSpacing(), 0);
constraints.anchor = GridBagConstraints.LINE_START;
CommunicationArtifactViewerHelper.addComponent(this, gridBagLayout, constraints, accountsStatus); CommunicationArtifactViewerHelper.addComponent(this, gridBagLayout, constraints, accountsStatus);
constraints.insets = savedInsets; constraints.insets = savedInsets;
@ -501,7 +506,6 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
constraints.gridy++; constraints.gridy++;
// this needs an extra indent // this needs an extra indent
constraints.insets = extraIndentInsets;
CommunicationArtifactViewerHelper.addKeyAtCol(this, gridBagLayout, constraints, Bundle.ContactArtifactViewer_missing_account_label(), 1); CommunicationArtifactViewerHelper.addKeyAtCol(this, gridBagLayout, constraints, Bundle.ContactArtifactViewer_missing_account_label(), 1);
constraints.insets = savedInsets; constraints.insets = savedInsets;
@ -544,12 +548,12 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
m_gridBagLayout = new GridBagLayout(); m_gridBagLayout = new GridBagLayout();
m_constraints = new GridBagConstraints(); m_constraints = new GridBagConstraints();
m_constraints.anchor = GridBagConstraints.FIRST_LINE_START; m_constraints.anchor = GridBagConstraints.LINE_START;
m_constraints.gridy = 0; m_constraints.gridy = 0;
m_constraints.gridx = 0; m_constraints.gridx = 0;
m_constraints.weighty = 0.0; m_constraints.weighty = 0.0;
m_constraints.weightx = 0.0; // keep components fixed horizontally. m_constraints.weightx = 0.0; // keep components fixed horizontally.
m_constraints.insets = new java.awt.Insets(0, CommunicationArtifactViewerHelper.LEFT_INSET, 0, 0); m_constraints.insets = new java.awt.Insets(0, ContentViewerDefaults.getSectionIndent(), 0, 0);
m_constraints.fill = GridBagConstraints.NONE; m_constraints.fill = GridBagConstraints.NONE;
} }
@ -560,7 +564,7 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
* @param artifact * @param artifact
* *
* @return Image from a TSK_CONTACT artifact or default image if none was * @return Image from a TSK_CONTACT artifact or default image if none was
* found or the artifact is not a TSK_CONTACT * found or the artifact is not a TSK_CONTACT
*/ */
private ImageIcon getImageFromArtifact(BlackboardArtifact artifact) { private ImageIcon getImageFromArtifact(BlackboardArtifact artifact) {
ImageIcon imageIcon = defaultImage; ImageIcon imageIcon = defaultImage;
@ -610,7 +614,7 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
* Creates a persona searcher task. * Creates a persona searcher task.
* *
* @param accountAttributesList List of attributes that may map to * @param accountAttributesList List of attributes that may map to
* accounts. * accounts.
*/ */
ContactPersonaSearcherTask(BlackboardArtifact artifact) { ContactPersonaSearcherTask(BlackboardArtifact artifact) {
this.artifact = artifact; this.artifact = artifact;
@ -640,7 +644,7 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
} }
} }
} }
Collection<PersonaAccount> personaAccounts = PersonaAccount.getPersonaAccountsForAccount(account); Collection<PersonaAccount> personaAccounts = PersonaAccount.getPersonaAccountsForAccount(account);
if (personaAccounts != null && !personaAccounts.isEmpty()) { if (personaAccounts != null && !personaAccounts.isEmpty()) {
// get personas for the account // get personas for the account
@ -709,7 +713,7 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
/** /**
* Constructor. * Constructor.
* *
* @param personaNameLabel Persona name label. * @param personaNameLabel Persona name label.
* @param personaActionButton Persona action button. * @param personaActionButton Persona action button.
*/ */
PersonaUIComponents(JLabel personaNameLabel, JButton personaActionButton) { PersonaUIComponents(JLabel personaNameLabel, JButton personaActionButton) {
@ -777,8 +781,8 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
for (CentralRepoAccount account : contactUniqueAccountsList) { for (CentralRepoAccount account : contactUniqueAccountsList) {
personaPanel.addAccount(account, Bundle.ContactArtifactViewer_persona_account_justification(), Persona.Confidence.HIGH); personaPanel.addAccount(account, Bundle.ContactArtifactViewer_persona_account_justification(), Persona.Confidence.HIGH);
} }
if(contactName != null && contactUniqueAccountsList.isEmpty()) { if (contactName != null && contactUniqueAccountsList.isEmpty()) {
createPersonaDialog.setStartupPopupMessage(Bundle.ContactArtifactViewer_id_not_found_in_cr(contactName)); createPersonaDialog.setStartupPopupMessage(Bundle.ContactArtifactViewer_id_not_found_in_cr(contactName));
} }

View File

@ -18,9 +18,9 @@
*/ */
package org.sleuthkit.autopsy.contentviewers.artifactviewers; package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints; import java.awt.GridBagConstraints;
import java.awt.GridBagLayout; import java.awt.GridBagLayout;
import java.awt.Insets; import java.awt.Insets;
@ -30,18 +30,24 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.JTextPane; import javax.swing.JTextPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
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.contentviewers.layout.ContentViewerDefaults;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
@ -55,13 +61,18 @@ import org.sleuthkit.datamodel.TskCoreException;
*/ */
@ServiceProvider(service = ArtifactContentViewer.class) @ServiceProvider(service = ArtifactContentViewer.class)
public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel implements ArtifactContentViewer { public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel implements ArtifactContentViewer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(GeneralPurposeArtifactViewer.class.getName()); private static final Logger logger = Logger.getLogger(GeneralPurposeArtifactViewer.class.getName());
// Number of columns in the gridbag layout. // Number of columns in the gridbag layout.
private final static int MAX_COLS = 4; private final static int MAX_COLS = 4;
private final static Insets ROW_INSETS = new java.awt.Insets(0, 12, 0, 0); private final static Insets ZERO_INSETS = new java.awt.Insets(0, 0, 0, 0);
private final static Insets HEADER_INSETS = new java.awt.Insets(0, 0, 0, 0);
private final static Insets FIRST_HEADER_INSETS = ZERO_INSETS;
private final static Insets HEADER_INSETS = new Insets(ContentViewerDefaults.getSectionSpacing(), 0, ContentViewerDefaults.getLineSpacing(), 0);
private final static Insets VALUE_COLUMN_INSETS = new Insets(0, ContentViewerDefaults.getColumnSpacing(), ContentViewerDefaults.getLineSpacing(), 0);
private final static Insets KEY_COLUMN_INSETS = new Insets(0, ContentViewerDefaults.getSectionIndent(), ContentViewerDefaults.getLineSpacing(), 0);
private final static double GLUE_WEIGHT_X = 1.0; private final static double GLUE_WEIGHT_X = 1.0;
private final static double TEXT_WEIGHT_X = 0.0; private final static double TEXT_WEIGHT_X = 0.0;
private final static int LABEL_COLUMN = 0; private final static int LABEL_COLUMN = 0;
@ -91,6 +102,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
initComponents(); initComponents();
gridBagConstraints.anchor = GridBagConstraints.FIRST_LINE_START; gridBagConstraints.anchor = GridBagConstraints.FIRST_LINE_START;
detailsPanel.setLayout(gridBagLayout); detailsPanel.setLayout(gridBagLayout);
detailsPanel.setBorder(new EmptyBorder(ContentViewerDefaults.getPanelInsets()));
} }
/** /**
@ -180,7 +192,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
gridBagConstraints.weighty = 0.0; gridBagConstraints.weighty = 0.0;
gridBagConstraints.weightx = TEXT_WEIGHT_X; // keep components fixed horizontally. gridBagConstraints.weightx = TEXT_WEIGHT_X; // keep components fixed horizontally.
gridBagConstraints.fill = GridBagConstraints.NONE; gridBagConstraints.fill = GridBagConstraints.NONE;
gridBagConstraints.insets = ROW_INSETS; gridBagConstraints.insets = ZERO_INSETS;
} }
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@ -386,29 +398,26 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
headingLabel.setEditable(false); headingLabel.setEditable(false);
// add a blank line before the start of new section, unless it's // add a blank line before the start of new section, unless it's
// the first section // the first section
if (gridBagConstraints.gridy != 0) { gridBagConstraints.insets = (gridBagConstraints.gridy == 0)
gridBagConstraints.gridy++; ? FIRST_HEADER_INSETS
// add to panel : HEADER_INSETS;
addToPanel(new javax.swing.JLabel(" "));
addLineEndGlue();
headingLabel.setFocusable(false);
}
gridBagConstraints.gridy++; gridBagConstraints.gridy++;
gridBagConstraints.gridx = LABEL_COLUMN;; gridBagConstraints.gridx = LABEL_COLUMN;;
// let the header span all of the row // let the header span all of the row
gridBagConstraints.gridwidth = MAX_COLS; gridBagConstraints.gridwidth = MAX_COLS;
gridBagConstraints.insets = HEADER_INSETS;
// set text // set text
headingLabel.setText(headerString); headingLabel.setText(headerString);
// make it large and bold // make it large and bold
headingLabel.setFont(headingLabel.getFont().deriveFont(Font.BOLD, headingLabel.getFont().getSize() + 2)); headingLabel.setFont(ContentViewerDefaults.getHeaderFont());
headingLabel.setMargin(ZERO_INSETS);
// add to panel // add to panel
addToPanel(headingLabel); addToPanel(headingLabel);
// reset constraints to normal // reset constraints to normal
gridBagConstraints.gridwidth = LABEL_WIDTH; gridBagConstraints.gridwidth = LABEL_WIDTH;
// add line end glue // add line end glue
addLineEndGlue(); addLineEndGlue();
gridBagConstraints.insets = ROW_INSETS; gridBagConstraints.insets = ZERO_INSETS;
return headingLabel; return headingLabel;
} }
@ -465,6 +474,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
javax.swing.JLabel keyLabel = new javax.swing.JLabel(); javax.swing.JLabel keyLabel = new javax.swing.JLabel();
keyLabel.setFocusable(false); keyLabel.setFocusable(false);
gridBagConstraints.gridy++; gridBagConstraints.gridy++;
gridBagConstraints.insets = KEY_COLUMN_INSETS;
gridBagConstraints.gridx = LABEL_COLUMN; gridBagConstraints.gridx = LABEL_COLUMN;
gridBagConstraints.gridwidth = LABEL_WIDTH; gridBagConstraints.gridwidth = LABEL_WIDTH;
// set text // set text
@ -492,7 +502,9 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
valueField.setFocusable(false); valueField.setFocusable(false);
valueField.setEditable(false); valueField.setEditable(false);
valueField.setOpaque(false); valueField.setOpaque(false);
valueField.setMargin(ZERO_INSETS);
gridBagConstraints.gridx = VALUE_COLUMN; gridBagConstraints.gridx = VALUE_COLUMN;
gridBagConstraints.insets = VALUE_COLUMN_INSETS;
GridBagConstraints cloneConstraints = (GridBagConstraints) gridBagConstraints.clone(); GridBagConstraints cloneConstraints = (GridBagConstraints) gridBagConstraints.clone();
// let the value span 2 cols // let the value span 2 cols
cloneConstraints.gridwidth = VALUE_WIDTH; cloneConstraints.gridwidth = VALUE_WIDTH;

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.contentviewers.artifactviewers; package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
@ -44,6 +45,7 @@ import javax.swing.JTextPane;
import javax.swing.LayoutStyle.ComponentPlacement; import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
@ -56,6 +58,7 @@ import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallback; import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallback;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode; import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel; import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel;
import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerDefaults;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.guiutils.ContactCache; import org.sleuthkit.autopsy.guiutils.ContactCache;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
@ -77,6 +80,15 @@ final class MessageAccountPanel extends JPanel {
private AccountFetcher currentFetcher = null; private AccountFetcher currentFetcher = null;
/**
* Main constructor.
*/
MessageAccountPanel() {
this.setBorder(new EmptyBorder(ContentViewerDefaults.getPanelInsets()));
}
/** /**
* Set the new artifact for the panel. * Set the new artifact for the panel.
* *
@ -170,9 +182,7 @@ final class MessageAccountPanel extends JPanel {
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(Alignment.LEADING) layout.createParallelGroup(Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addGroup(getMainHorizontalGroup(layout, dataList))));
.addGroup(getMainHorizontalGroup(layout, dataList))
.addContainerGap(158, Short.MAX_VALUE)));
layout.setVerticalGroup(getMainVerticalGroup(layout, dataList)); layout.setVerticalGroup(getMainVerticalGroup(layout, dataList));
setLayout(layout); setLayout(layout);
@ -186,6 +196,7 @@ final class MessageAccountPanel extends JPanel {
messageLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); messageLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
messageLabel.setText(Bundle.MessageAccountPanel_no_matches()); messageLabel.setText(Bundle.MessageAccountPanel_no_matches());
messageLabel.setFont(ContentViewerDefaults.getMessageFont());
messageLabel.setEnabled(false); messageLabel.setEnabled(false);
messagePanel.add(messageLabel, java.awt.BorderLayout.CENTER); messagePanel.add(messageLabel, java.awt.BorderLayout.CENTER);
@ -224,14 +235,12 @@ final class MessageAccountPanel extends JPanel {
private ParallelGroup getMainVerticalGroup(GroupLayout layout, List<AccountContainer> data) { private ParallelGroup getMainVerticalGroup(GroupLayout layout, List<AccountContainer> data) {
SequentialGroup group = layout.createSequentialGroup(); SequentialGroup group = layout.createSequentialGroup();
for (AccountContainer o : data) { for (AccountContainer o : data) {
group.addGap(5) group.addComponent(o.getAccountLabel())
.addComponent(o.getAccountLabel())
.addGroup(o.getContactLineVerticalGroup(layout)) .addGroup(o.getContactLineVerticalGroup(layout))
.addGroup(o.getPersonLineVerticalGroup(layout)); .addGroup(o.getPersonLineVerticalGroup(layout));
group.addGap(ContentViewerDefaults.getSectionSpacing());
} }
group.addContainerGap(83, Short.MAX_VALUE);
return layout.createParallelGroup().addGroup(group); return layout.createParallelGroup().addGroup(group);
} }
@ -259,12 +268,11 @@ final class MessageAccountPanel extends JPanel {
private SequentialGroup getPersonaHorizontalGroup(GroupLayout layout, List<AccountContainer> data) { private SequentialGroup getPersonaHorizontalGroup(GroupLayout layout, List<AccountContainer> data) {
SequentialGroup group = layout.createSequentialGroup(); SequentialGroup group = layout.createSequentialGroup();
ParallelGroup pgroup = layout.createParallelGroup(Alignment.LEADING); ParallelGroup pgroup = layout.createParallelGroup(Alignment.LEADING);
group.addGap(10);
for (AccountContainer o : data) { for (AccountContainer o : data) {
pgroup.addGroup(o.getPersonaSequentialGroup(layout)); pgroup.addGroup(o.getPersonaSequentialGroup(layout));
pgroup.addGroup(o.getContactSequentialGroup(layout)); pgroup.addGroup(o.getContactSequentialGroup(layout));
} }
group.addGap(10) group.addGap(ContentViewerDefaults.getSectionIndent())
.addGroup(pgroup) .addGroup(pgroup)
.addPreferredGap(ComponentPlacement.RELATED) .addPreferredGap(ComponentPlacement.RELATED)
.addGroup(getButtonGroup(layout, data)); .addGroup(getButtonGroup(layout, data));
@ -343,7 +351,9 @@ final class MessageAccountPanel extends JPanel {
button = new JButton(); button = new JButton();
button.addActionListener(new PersonaButtonListener(this)); button.addActionListener(new PersonaButtonListener(this));
accountLabel.setMargin(new Insets(0, 0, 0, 0));
accountLabel.setText(account.getTypeSpecificID()); accountLabel.setText(account.getTypeSpecificID());
accountLabel.setFont(ContentViewerDefaults.getHeaderFont());
contactDisplayName.setText(contactName); contactDisplayName.setText(contactName);
personaDisplayName.setText(persona != null ? persona.getName() : Bundle.MessageAccountPanel_unknown_label()); personaDisplayName.setText(persona != null ? persona.getName() : Bundle.MessageAccountPanel_unknown_label());

View File

@ -2,11 +2,17 @@
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties> <Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor"> <Property name="background" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Color blue="ff" green="ff" red="ff" type="rgb"/> <Connection code="ContentViewerDefaults.getPanelBackground()" type="code"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[495, 55]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 55]"/>
</Property> </Property>
<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="[495, 75]"/> <Dimension value="[495, 55]"/>
</Property> </Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
@ -24,15 +30,13 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/>
<Component id="jSourceNameLabel" min="-2" max="-2" attributes="0"/> <Component id="jSourceNameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="36" max="-2" attributes="0"/> <EmptySpace min="-2" pref="36" max="-2" attributes="0"/>
<Component id="jSourceTextLabel" max="32767" attributes="0"/> <Component id="jSourceTextLabel" pref="360" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="260" max="-2" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="90" max="-2" attributes="0"/> <EmptySpace min="-2" pref="40" max="-2" attributes="0"/>
<Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/> <Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/> <EmptySpace max="32767" attributes="0"/>
</Group> </Group>
@ -41,14 +45,13 @@
<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">
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="jSourceNameLabel" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="jSourceNameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jSourceTextLabel" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="jSourceTextLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/> <Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/> <EmptySpace min="0" pref="11" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.contentviewers.contextviewer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.sleuthkit.autopsy.contentviewers.contextviewer.ContextViewer.DateTimePanel; import org.sleuthkit.autopsy.contentviewers.contextviewer.ContextViewer.DateTimePanel;
import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerDefaults;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
@ -75,8 +76,10 @@ public final class ContextSourcePanel extends javax.swing.JPanel implements Date
jSourceNameLabel = new javax.swing.JLabel(); jSourceNameLabel = new javax.swing.JLabel();
jSourceTextLabel = new javax.swing.JLabel(); jSourceTextLabel = new javax.swing.JLabel();
setBackground(new java.awt.Color(255, 255, 255)); setBackground(ContentViewerDefaults.getPanelBackground());
setPreferredSize(new java.awt.Dimension(495, 75)); setMaximumSize(new java.awt.Dimension(495, 55));
setMinimumSize(new java.awt.Dimension(300, 55));
setPreferredSize(new java.awt.Dimension(495, 55));
org.openide.awt.Mnemonics.setLocalizedText(jSourceGoToResultButton, org.openide.util.NbBundle.getMessage(ContextSourcePanel.class, "ContextSourcePanel.jSourceGoToResultButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jSourceGoToResultButton, org.openide.util.NbBundle.getMessage(ContextSourcePanel.class, "ContextSourcePanel.jSourceGoToResultButton.text")); // NOI18N
jSourceGoToResultButton.addActionListener(new java.awt.event.ActionListener() { jSourceGoToResultButton.addActionListener(new java.awt.event.ActionListener() {
@ -94,26 +97,23 @@ public final class ContextSourcePanel extends javax.swing.JPanel implements Date
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(50, 50, 50)
.addComponent(jSourceNameLabel) .addComponent(jSourceNameLabel)
.addGap(36, 36, 36) .addGap(36, 36, 36)
.addComponent(jSourceTextLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jSourceTextLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 360, Short.MAX_VALUE))
.addGap(260, 260, 260))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(90, 90, 90) .addGap(40, 40, 40)
.addComponent(jSourceGoToResultButton) .addComponent(jSourceGoToResultButton)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap(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()
.addGap(2, 2, 2)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jSourceNameLabel) .addComponent(jSourceNameLabel)
.addComponent(jSourceTextLabel)) .addComponent(jSourceTextLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSourceGoToResultButton) .addComponent(jSourceGoToResultButton)
.addGap(0, 0, 0)) .addGap(0, 11, Short.MAX_VALUE))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents

View File

@ -2,11 +2,17 @@
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties> <Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor"> <Property name="background" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Color blue="ff" green="ff" red="ff" type="rgb"/> <Connection code="ContentViewerDefaults.getPanelBackground()" type="code"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 55]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 55]"/>
</Property> </Property>
<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="[495, 75]"/> <Dimension value="[495, 55]"/>
</Property> </Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
@ -24,30 +30,28 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/>
<Component id="jUsageNameLabel" min="-2" max="-2" attributes="0"/> <Component id="jUsageNameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="jUsageTextLabel" pref="240" max="32767" attributes="0"/> <Component id="jUsageTextLabel" pref="420" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="36" max="-2" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="90" max="-2" attributes="0"/> <EmptySpace min="-2" pref="40" max="-2" attributes="0"/>
<Component id="jUsageGoToResultButton" min="-2" max="-2" attributes="0"/> <Component id="jUsageGoToResultButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/> <EmptySpace pref="275" max="-2" attributes="0"/>
</Group> </Group>
</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">
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="jUsageTextLabel" min="-2" max="-2" attributes="0"/> <Component id="jUsageTextLabel" min="-2" max="-2" attributes="0"/>
<Component id="jUsageNameLabel" alignment="1" min="-2" max="-2" attributes="0"/> <Component id="jUsageNameLabel" alignment="1" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="jUsageGoToResultButton" min="-2" max="-2" attributes="0"/> <Component id="jUsageGoToResultButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="11" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.contentviewers.contextviewer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerDefaults;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
@ -74,8 +75,10 @@ public final class ContextUsagePanel extends javax.swing.JPanel implements Conte
jUsageNameLabel = new javax.swing.JLabel(); jUsageNameLabel = new javax.swing.JLabel();
jUsageTextLabel = new javax.swing.JLabel(); jUsageTextLabel = new javax.swing.JLabel();
setBackground(new java.awt.Color(255, 255, 255)); setBackground(ContentViewerDefaults.getPanelBackground());
setPreferredSize(new java.awt.Dimension(495, 75)); setMaximumSize(new java.awt.Dimension(32767, 55));
setMinimumSize(new java.awt.Dimension(300, 55));
setPreferredSize(new java.awt.Dimension(495, 55));
org.openide.awt.Mnemonics.setLocalizedText(jUsageGoToResultButton, org.openide.util.NbBundle.getMessage(ContextUsagePanel.class, "ContextUsagePanel.jUsageGoToResultButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jUsageGoToResultButton, org.openide.util.NbBundle.getMessage(ContextUsagePanel.class, "ContextUsagePanel.jUsageGoToResultButton.text")); // NOI18N
jUsageGoToResultButton.addActionListener(new java.awt.event.ActionListener() { jUsageGoToResultButton.addActionListener(new java.awt.event.ActionListener() {
@ -93,25 +96,23 @@ public final class ContextUsagePanel extends javax.swing.JPanel implements Conte
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(50, 50, 50)
.addComponent(jUsageNameLabel) .addComponent(jUsageNameLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jUsageTextLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 240, Short.MAX_VALUE) .addComponent(jUsageTextLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 420, Short.MAX_VALUE))
.addGap(36, 36, 36))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(90, 90, 90) .addGap(40, 40, 40)
.addComponent(jUsageGoToResultButton) .addComponent(jUsageGoToResultButton)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap(275, javax.swing.GroupLayout.PREFERRED_SIZE))
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(2, 2, 2)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jUsageTextLabel) .addComponent(jUsageTextLabel)
.addComponent(jUsageNameLabel, javax.swing.GroupLayout.Alignment.TRAILING)) .addComponent(jUsageNameLabel, javax.swing.GroupLayout.Alignment.TRAILING))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jUsageGoToResultButton)) .addComponent(jUsageGoToResultButton)
.addGap(0, 11, Short.MAX_VALUE))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents

View File

@ -4,38 +4,19 @@
<NonVisualComponents> <NonVisualComponents>
<Container class="javax.swing.JPanel" name="jSourcePanel"> <Container class="javax.swing.JPanel" name="jSourcePanel">
<Properties> <Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor"> <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Color blue="ff" green="ff" id="window" palette="3" red="ff" type="palette"/> <Connection code="new EmptyBorder(FIRST_HEADER_INSETS)" type="code"/>
</Property> </Property>
</Properties> </Properties>
<Layout> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<DimensionLayout dim="0"> <Property name="axis" type="int" value="3"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="40" max="-2" attributes="0"/>
<Component id="jSourceLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="304" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
<Component id="jSourceLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout> </Layout>
<SubComponents> <SubComponents>
<Component class="javax.swing.JLabel" name="jSourceLabel"> <Component class="javax.swing.JLabel" name="jSourceLabel">
<Properties> <Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor"> <Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<FontInfo relative="true"> <Connection code="ContentViewerDefaults.getHeaderFont()" type="code"/>
<Font bold="true" component="jSourceLabel" property="font" relativeSize="true" size="1"/>
</FontInfo>
</Property> </Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jSourceLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jSourceLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
@ -50,38 +31,19 @@
</Container> </Container>
<Container class="javax.swing.JPanel" name="jUsagePanel"> <Container class="javax.swing.JPanel" name="jUsagePanel">
<Properties> <Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor"> <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Color blue="ff" green="ff" id="window" palette="3" red="ff" type="palette"/> <Connection code="new EmptyBorder(HEADER_INSETS)" type="code"/>
</Property> </Property>
</Properties> </Properties>
<Layout> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<DimensionLayout dim="0"> <Property name="axis" type="int" value="3"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="40" max="-2" attributes="0"/>
<Component id="jUsageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="298" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Component id="jUsageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout> </Layout>
<SubComponents> <SubComponents>
<Component class="javax.swing.JLabel" name="jUsageLabel"> <Component class="javax.swing.JLabel" name="jUsageLabel">
<Properties> <Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor"> <Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<FontInfo relative="true"> <Connection code="ContentViewerDefaults.getHeaderFont()&#xd;&#xa;" type="code"/>
<Font bold="true" component="jUsageLabel" property="font" relativeSize="true" size="1"/>
</FontInfo>
</Property> </Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jUsageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jUsageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
@ -95,31 +57,9 @@
</SubComponents> </SubComponents>
</Container> </Container>
<Container class="javax.swing.JPanel" name="jUnknownPanel"> <Container class="javax.swing.JPanel" name="jUnknownPanel">
<Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="ff" green="ff" red="ff" type="rgb"/>
</Property>
</Properties>
<Layout> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<DimensionLayout dim="0"> <Property name="axis" type="int" value="3"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/>
<Component id="jUnknownLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Component id="jUnknownLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout> </Layout>
<SubComponents> <SubComponents>
<Component class="javax.swing.JLabel" name="jUnknownLabel"> <Component class="javax.swing.JLabel" name="jUnknownLabel">
@ -127,6 +67,9 @@
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jUnknownLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jUnknownLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new EmptyBorder(DATA_ROW_INSETS)" type="code"/>
</Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/> <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
@ -137,11 +80,8 @@
</Container> </Container>
</NonVisualComponents> </NonVisualComponents>
<Properties> <Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="ff" green="ff" red="ff" type="rgb"/>
</Property>
<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="[495, 358]"/> <Dimension value="[0, 0]"/>
</Property> </Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
@ -171,8 +111,8 @@
<SubComponents> <SubComponents>
<Container class="javax.swing.JScrollPane" name="jScrollPane"> <Container class="javax.swing.JScrollPane" name="jScrollPane">
<Properties> <Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Color blue="ff" green="ff" red="ff" type="rgb"/> <Dimension value="[16, 16]"/>
</Property> </Property>
</Properties> </Properties>

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.contentviewers.contextviewer; package org.sleuthkit.autopsy.contentviewers.contextviewer;
import java.awt.Component; import java.awt.Component;
import java.awt.Insets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -28,12 +29,14 @@ import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED; import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED;
import javax.swing.border.EmptyBorder;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.openide.nodes.Node; import org.openide.nodes.Node;
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.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerDefaults;
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.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
@ -55,6 +58,10 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
private static final Logger logger = Logger.getLogger(ContextViewer.class.getName()); private static final Logger logger = Logger.getLogger(ContextViewer.class.getName());
private static final int ARTIFACT_STR_MAX_LEN = 1024; private static final int ARTIFACT_STR_MAX_LEN = 1024;
private static final int ATTRIBUTE_STR_MAX_LEN = 200; private static final int ATTRIBUTE_STR_MAX_LEN = 200;
private final static Insets FIRST_HEADER_INSETS = new Insets(0, 0, 0, 0);
private final static Insets HEADER_INSETS = new Insets(ContentViewerDefaults.getSectionSpacing(), 0, ContentViewerDefaults.getLineSpacing(), 0);
private final static Insets DATA_ROW_INSETS = new Insets(0, ContentViewerDefaults.getSectionIndent(), ContentViewerDefaults.getLineSpacing(), 0);
// defines a list of artifacts that provide context for a file // defines a list of artifacts that provide context for a file
private static final List<BlackboardArtifact.ARTIFACT_TYPE> CONTEXT_ARTIFACTS = new ArrayList<>(); private static final List<BlackboardArtifact.ARTIFACT_TYPE> CONTEXT_ARTIFACTS = new ArrayList<>();
@ -91,75 +98,30 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
javax.swing.JLabel jUnknownLabel = new javax.swing.JLabel(); javax.swing.JLabel jUnknownLabel = new javax.swing.JLabel();
jScrollPane = new javax.swing.JScrollPane(); jScrollPane = new javax.swing.JScrollPane();
jSourcePanel.setBackground(javax.swing.UIManager.getDefaults().getColor("window")); jSourcePanel.setBorder(new EmptyBorder(FIRST_HEADER_INSETS));
jSourcePanel.setLayout(new javax.swing.BoxLayout(jSourcePanel, javax.swing.BoxLayout.PAGE_AXIS));
jSourceLabel.setFont(jSourceLabel.getFont().deriveFont(jSourceLabel.getFont().getStyle() | java.awt.Font.BOLD, jSourceLabel.getFont().getSize()+1)); jSourceLabel.setFont(ContentViewerDefaults.getHeaderFont());
org.openide.awt.Mnemonics.setLocalizedText(jSourceLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jSourceLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceLabel.text")); // NOI18N
jSourcePanel.add(jSourceLabel);
javax.swing.GroupLayout jSourcePanelLayout = new javax.swing.GroupLayout(jSourcePanel); jUsagePanel.setBorder(new EmptyBorder(HEADER_INSETS));
jSourcePanel.setLayout(jSourcePanelLayout); jUsagePanel.setLayout(new javax.swing.BoxLayout(jUsagePanel, javax.swing.BoxLayout.PAGE_AXIS));
jSourcePanelLayout.setHorizontalGroup(
jSourcePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) jUsageLabel.setFont(ContentViewerDefaults.getHeaderFont()
.addGroup(jSourcePanelLayout.createSequentialGroup()
.addGap(40, 40, 40)
.addComponent(jSourceLabel)
.addContainerGap(304, Short.MAX_VALUE))
); );
jSourcePanelLayout.setVerticalGroup(
jSourcePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jSourcePanelLayout.createSequentialGroup()
.addGap(5, 5, 5)
.addComponent(jSourceLabel)
.addGap(2, 2, 2))
);
jUsagePanel.setBackground(javax.swing.UIManager.getDefaults().getColor("window"));
jUsageLabel.setFont(jUsageLabel.getFont().deriveFont(jUsageLabel.getFont().getStyle() | java.awt.Font.BOLD, jUsageLabel.getFont().getSize()+1));
org.openide.awt.Mnemonics.setLocalizedText(jUsageLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jUsageLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jUsageLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jUsageLabel.text")); // NOI18N
jUsagePanel.add(jUsageLabel);
javax.swing.GroupLayout jUsagePanelLayout = new javax.swing.GroupLayout(jUsagePanel); jUnknownPanel.setLayout(new javax.swing.BoxLayout(jUnknownPanel, javax.swing.BoxLayout.PAGE_AXIS));
jUsagePanel.setLayout(jUsagePanelLayout);
jUsagePanelLayout.setHorizontalGroup(
jUsagePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jUsagePanelLayout.createSequentialGroup()
.addGap(40, 40, 40)
.addComponent(jUsageLabel)
.addContainerGap(298, Short.MAX_VALUE))
);
jUsagePanelLayout.setVerticalGroup(
jUsagePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jUsagePanelLayout.createSequentialGroup()
.addGap(2, 2, 2)
.addComponent(jUsageLabel)
.addGap(2, 2, 2))
);
jUnknownPanel.setBackground(new java.awt.Color(255, 255, 255));
org.openide.awt.Mnemonics.setLocalizedText(jUnknownLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jUnknownLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jUnknownLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jUnknownLabel.text")); // NOI18N
jUnknownLabel.setBorder(new EmptyBorder(DATA_ROW_INSETS));
jUnknownPanel.add(jUnknownLabel);
javax.swing.GroupLayout jUnknownPanelLayout = new javax.swing.GroupLayout(jUnknownPanel); setPreferredSize(new java.awt.Dimension(0, 0));
jUnknownPanel.setLayout(jUnknownPanelLayout);
jUnknownPanelLayout.setHorizontalGroup(
jUnknownPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jUnknownPanelLayout.createSequentialGroup()
.addGap(50, 50, 50)
.addComponent(jUnknownLabel)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
jUnknownPanelLayout.setVerticalGroup(
jUnknownPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jUnknownPanelLayout.createSequentialGroup()
.addGap(2, 2, 2)
.addComponent(jUnknownLabel)
.addGap(2, 2, 2))
);
setBackground(new java.awt.Color(255, 255, 255)); jScrollPane.setPreferredSize(new java.awt.Dimension(16, 16));
setPreferredSize(new java.awt.Dimension(495, 358));
jScrollPane.setBackground(new java.awt.Color(255, 255, 255));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
@ -275,13 +237,17 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
} }
} }
javax.swing.JPanel contextContainer = new javax.swing.JPanel(); javax.swing.JPanel contextContainer = new javax.swing.JPanel();
contextContainer.add(jSourcePanel);
contextContainer.setLayout(new BoxLayout(contextContainer, BoxLayout.Y_AXIS)); contextContainer.setLayout(new BoxLayout(contextContainer, BoxLayout.Y_AXIS));
contextContainer.setBorder(new EmptyBorder(ContentViewerDefaults.getPanelInsets()));
contextContainer.add(jSourcePanel);
if (contextSourcePanels.isEmpty()) { if (contextSourcePanels.isEmpty()) {
contextContainer.add(jUnknownPanel); contextContainer.add(jUnknownPanel);
} else { } else {
for (javax.swing.JPanel sourcePanel : contextSourcePanels) { for (javax.swing.JPanel sourcePanel : contextSourcePanels) {
contextContainer.add(sourcePanel); contextContainer.add(sourcePanel);
contextContainer.setAlignmentX(0);
} }
} }
contextContainer.add(jUsagePanel); contextContainer.add(jUsagePanel);
@ -290,10 +256,11 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
} else { } else {
for (javax.swing.JPanel usagePanel : contextUsagePanels) { for (javax.swing.JPanel usagePanel : contextUsagePanels) {
contextContainer.add(usagePanel); contextContainer.add(usagePanel);
contextContainer.setAlignmentX(0);
} }
} }
contextContainer.setBackground(javax.swing.UIManager.getDefaults().getColor("window")); contextContainer.setBackground(ContentViewerDefaults.getPanelBackground());
contextContainer.setEnabled(foundASource); contextContainer.setEnabled(foundASource);
contextContainer.setVisible(foundASource); contextContainer.setVisible(foundASource);
jScrollPane.getViewport().setView(contextContainer); jScrollPane.getViewport().setView(contextContainer);
@ -346,6 +313,8 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
String sourceName = Bundle.ContextViewer_attachmentSource(); String sourceName = Bundle.ContextViewer_attachmentSource();
String sourceText = msgArtifactToAbbreviatedString(associatedArtifact); String sourceText = msgArtifactToAbbreviatedString(associatedArtifact);
ContextSourcePanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact, dateTime); ContextSourcePanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact, dateTime);
sourcePanel.setBorder(new EmptyBorder(DATA_ROW_INSETS));
sourcePanel.setAlignmentX(0);
contextSourcePanels.add(sourcePanel); contextSourcePanels.add(sourcePanel);
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == associatedArtifact.getArtifactTypeID() } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == associatedArtifact.getArtifactTypeID()
@ -353,18 +322,24 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
String sourceName = Bundle.ContextViewer_downloadSource(); String sourceName = Bundle.ContextViewer_downloadSource();
String sourceText = webDownloadArtifactToString(associatedArtifact); String sourceText = webDownloadArtifactToString(associatedArtifact);
ContextSourcePanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact, dateTime); ContextSourcePanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact, dateTime);
sourcePanel.setBorder(new EmptyBorder(DATA_ROW_INSETS));
sourcePanel.setAlignmentX(0);
contextSourcePanels.add(sourcePanel); contextSourcePanels.add(sourcePanel);
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID() == associatedArtifact.getArtifactTypeID()) { } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID() == associatedArtifact.getArtifactTypeID()) {
String sourceName = Bundle.ContextViewer_recentDocs(); String sourceName = Bundle.ContextViewer_recentDocs();
String sourceText = recentDocArtifactToString(associatedArtifact); String sourceText = recentDocArtifactToString(associatedArtifact);
ContextUsagePanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact, dateTime); ContextUsagePanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact, dateTime);
usagePanel.setBorder(new EmptyBorder(DATA_ROW_INSETS));
usagePanel.setAlignmentX(0);
contextUsagePanels.add(usagePanel); contextUsagePanels.add(usagePanel);
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() == associatedArtifact.getArtifactTypeID()) { } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() == associatedArtifact.getArtifactTypeID()) {
String sourceName = Bundle.ContextViewer_programExecution(); String sourceName = Bundle.ContextViewer_programExecution();
String sourceText = programExecArtifactToString(associatedArtifact); String sourceText = programExecArtifactToString(associatedArtifact);
ContextUsagePanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact, dateTime); ContextUsagePanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact, dateTime);
usagePanel.setBorder(new EmptyBorder(DATA_ROW_INSETS));
usagePanel.setAlignmentX(0);
contextUsagePanels.add(usagePanel); contextUsagePanels.add(usagePanel);
} }

View File

@ -0,0 +1,167 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 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.layout;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.Color;
import javax.swing.UIManager;
/**
* Default values for layout of content values.
*/
public class ContentViewerDefaults {
private static final Font DEFAULT_FONT = UIManager.getDefaults().getFont("Label.font");
// based on https://stackoverflow.com/questions/5829703/java-getting-a-font-with-a-specific-height-in-pixels/26564924#26564924
private static final Double PT_TO_PX = Toolkit.getDefaultToolkit().getScreenResolution() / 72.0;
private static final int DEFAULT_FONT_PX = (int) Math.round(DEFAULT_FONT.getSize() * PT_TO_PX);
private static final Font SUB_HEADER_FONT = DEFAULT_FONT.deriveFont(Font.BOLD);
private static final Font HEADER_FONT = DEFAULT_FONT.deriveFont(Font.BOLD, DEFAULT_FONT.getSize() + 2);
private static final Font MESSAGE_FONT = DEFAULT_FONT.deriveFont(Font.ITALIC);
private static final Font MONOSPACED_FONT = new Font(Font.MONOSPACED, Font.PLAIN, DEFAULT_FONT.getSize());
private static final Insets DEFAULT_PANEL_INSETS = UIManager.getDefaults().getInsets("TextPane.margin");
private static final int DEFAULT_INDENT = DEFAULT_FONT_PX;
private static final int DEFAULT_SECTION_SPACING = DEFAULT_FONT_PX;
private static final int DEFAULT_COLUMN_SPACING = (int) Math.round((double) DEFAULT_FONT_PX / 3);
private static final int DEFAULT_LINE_SPACING = (int) Math.round((double) DEFAULT_FONT_PX / 5);
private static final Color DEFAULT_BACKGROUND = UIManager.getColor("Panel.background");
/**
* Returns the horizontal spacing between columns in a table in pixels.
*
* @return The horizontal spacing between columns in a table in pixels.
*/
public static int getColumnSpacing() {
return DEFAULT_COLUMN_SPACING;
}
/**
* Returns the default font to be used.
*
* @return the default font to be used.
*/
public static Font getFont() {
return DEFAULT_FONT;
}
/**
* Returns the font to be displayed for messages.
*
* @return The font to be displayed for messages.
*/
public static Font getMessageFont() {
return MESSAGE_FONT;
}
/**
* Returns the font to be displayed for messages.
*
* @return The font to be displayed for messages.
*/
public static Font getHeaderFont() {
return HEADER_FONT;
}
/**
* Returns the font to be displayed for sub headers.
*
* @return The font to be displayed for sub headers.
*/
public static Font getSubHeaderFont() {
return SUB_HEADER_FONT;
}
/**
* Returns the font to be used for normal monospace.
*
* @return The font to be used for normal monospace.
*/
public static Font getMonospacedFont() {
return MONOSPACED_FONT;
}
/**
* Returns the insets of the content within the parent content viewer panel.
*
* @return The insets of the content within the parent content viewer panel.
*/
public static Insets getPanelInsets() {
return DEFAULT_PANEL_INSETS;
}
/**
* Returns the size in pixels that sections should be indented.
*
* @return The size in pixels that sections should be indented.
*/
public static Integer getSectionIndent() {
return DEFAULT_INDENT;
}
/**
* Returns the spacing between sections in pixels.
*
* @return The spacing between sections in pixels.
*/
public static Integer getSectionSpacing() {
return DEFAULT_SECTION_SPACING;
}
/**
* Returns the spacing between lines of text in pixels.
*
* @return The spacing between lines of text in pixels.
*/
public static Integer getLineSpacing() {
return DEFAULT_LINE_SPACING;
}
/**
* Returns the color to be used as the background of the panel.
*
* @return The color to be used as the background of the panel.
*/
public static Color getPanelBackground() {
return DEFAULT_BACKGROUND;
}
/**
* Returns the ratio of point size to pixel size for the user's screen
* resolution.
*
* @return The ratio of point size to pixel size for the user's screen
* resolution.
*/
public static Double getPtToPx() {
return PT_TO_PX;
}
}

View File

@ -0,0 +1,194 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 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.layout;
import java.awt.Font;
import javax.swing.JTextPane;
import javax.swing.text.EditorKit;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;
/**
* The style sheet an class names to be used with content viewers using html
* rendering.
*/
public class ContentViewerHtmlStyles {
// html stylesheet classnames for components
private static final String CLASS_PREFIX = ContentViewerHtmlStyles.class.getSimpleName();
private static final String HEADER_CLASSNAME = CLASS_PREFIX + "header";
private static final String SUB_HEADER_CLASSNAME = CLASS_PREFIX + "subHeader";
private static final String MESSAGE_CLASSNAME = CLASS_PREFIX + "message";
private static final String TEXT_CLASSNAME = CLASS_PREFIX + "text";
private static final String MONOSPACED_CLASSNAME = CLASS_PREFIX + "monospaced";
private static final String INDENTED_CLASSNAME = CLASS_PREFIX + "indent";
private static final String SPACED_SECTION_CLASSNAME = CLASS_PREFIX + "spacedSection";
private static final String KEY_COLUMN_TD_CLASSNAME = CLASS_PREFIX + "keyKolumn";
private static final Font DEFAULT_FONT = ContentViewerDefaults.getFont();
private static final Font MESSAGE_FONT = ContentViewerDefaults.getMessageFont();
private static final Font HEADER_FONT = ContentViewerDefaults.getHeaderFont();
private static final Font SUB_HEADER_FONT = ContentViewerDefaults.getSubHeaderFont();
private static final Font MONOSPACED_FONT = ContentViewerDefaults.getMonospacedFont();
// additional styling for components
private static final String STYLE_SHEET_RULE
= String.format(" .%s { font-family: %s; font-size: %dpt;font-style:italic; margin: 0px; padding: 0px 0px %dpt 0px; } ",
MESSAGE_CLASSNAME, MESSAGE_FONT.getFamily(), MESSAGE_FONT.getSize(), pxToPt(ContentViewerDefaults.getLineSpacing()))
+ String.format(" .%s { font-family: %s; font-size: %dpt; font-weight: bold; margin: 0px; padding: 0px 0px %dpt 0px; } ",
HEADER_CLASSNAME, HEADER_FONT.getFamily(), HEADER_FONT.getSize(), pxToPt(ContentViewerDefaults.getLineSpacing()))
+ String.format(" .%s { font-family: %s; font-size: %dpt; font-weight: bold; margin: 0px; padding: 0px 0px %dpt 0px; } ",
SUB_HEADER_CLASSNAME, SUB_HEADER_FONT.getFamily(), SUB_HEADER_FONT.getSize(), pxToPt(ContentViewerDefaults.getLineSpacing()))
+ String.format(" .%s { font-family: %s; font-size: %dpt; margin: 0px; padding: 0px 0px %dpt 0px; } ",
TEXT_CLASSNAME, DEFAULT_FONT.getFamily(), DEFAULT_FONT.getSize(), pxToPt(ContentViewerDefaults.getLineSpacing()))
+ String.format(" .%s { font-family: %s; font-size: %dpt; margin: 0px; padding: 0px 0px %dpt 0px; } ",
MONOSPACED_CLASSNAME, Font.MONOSPACED, MONOSPACED_FONT.getSize(), pxToPt(ContentViewerDefaults.getLineSpacing()))
+ String.format(" .%s { padding-left: %dpt } ",
INDENTED_CLASSNAME, pxToPt(ContentViewerDefaults.getSectionIndent()))
+ String.format(" .%s { padding-top: %dpt } ",
SPACED_SECTION_CLASSNAME, pxToPt(ContentViewerDefaults.getSectionSpacing()))
+ String.format(" .%s { padding-right: %dpt } ",
KEY_COLUMN_TD_CLASSNAME, pxToPt(ContentViewerDefaults.getColumnSpacing()));
private static final StyleSheet STYLE_SHEET = new StyleSheet();
static {
// add the style rule to the style sheet.
STYLE_SHEET.addRule(STYLE_SHEET_RULE);
}
/**
* Converts pixel size to point size. The html rendering seems more
* consistent with point size versus pixel size.
*
* @param px The pixel size.
*
* @return The point size.
*/
private static int pxToPt(int px) {
return (int) Math.round(((double) px) / ContentViewerDefaults.getPtToPx());
}
/**
* Returns the class name to use for header text.
*
* @return The class name to use for header text.
*/
public static String getHeaderClassName() {
return HEADER_CLASSNAME;
}
/**
* Returns the class name to use for sub header text.
*
* @return The class name to use for sub header text.
*/
public static String getSubHeaderClassName() {
return SUB_HEADER_CLASSNAME;
}
/**
* Returns the class name to use for message text.
*
* @return The class name to use for message text.
*/
public static String getMessageClassName() {
return MESSAGE_CLASSNAME;
}
/**
* Returns the class name to use for regular text.
*
* @return The class name to use for regular text.
*/
public static String getTextClassName() {
return TEXT_CLASSNAME;
}
/**
* Returns the class name to use for monospaced text.
*
* @return The class name to use for monospaced text.
*/
public static String getMonospacedClassName() {
return MONOSPACED_CLASSNAME;
}
/**
* Returns the class name to use for an indented (left padding) section.
*
* @return The class name to use for an indented (left padding) section.
*/
public static String getIndentedClassName() {
return INDENTED_CLASSNAME;
}
/**
* Returns the class name to use for a section with spacing (top padding)
* section.
*
* @return The class name to use for a section with spacing (top padding)
* section.
*/
public static String getSpacedSectionClassName() {
return SPACED_SECTION_CLASSNAME;
}
/**
* Returns the class name to use for a key column with right spacing (right
* padding).
*
* @return The class name to use for a key column with right spacing (right
* padding).
*/
public static String getKeyColumnClassName() {
return KEY_COLUMN_TD_CLASSNAME;
}
/**
* If the textPane has an HTMLEditorKit, specifies the
* ContentViewerHTMLStyles styles to use refreshing the styles.
*
* @param textPane The text pane.
*/
public static void setStyles(JTextPane textPane) {
EditorKit editorKit = textPane.getEditorKit();
if (editorKit instanceof HTMLEditorKit) {
((HTMLEditorKit) editorKit).setStyleSheet(STYLE_SHEET);
}
}
/**
* Sets up a JTextPane for html rendering using the css class names
* specified in this class.
*
* @param textPane The JTextPane to set up for content viewer html
* rendering.
*/
public static void setupHtmlJTextPane(JTextPane textPane) {
textPane.setContentType("text/html;charset=UTF-8"); //NON-NLS
HTMLEditorKit kit = new HTMLEditorKit();
textPane.setEditorKit(kit);
kit.setStyleSheet(STYLE_SHEET);
textPane.setMargin(ContentViewerDefaults.getPanelInsets());
textPane.setBackground(ContentViewerDefaults.getPanelBackground());
}
}

View File

@ -19,7 +19,6 @@
package org.sleuthkit.autopsy.contentviewers.osaccount; package org.sleuthkit.autopsy.contentviewers.osaccount;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GridBagConstraints; import java.awt.GridBagConstraints;
import java.awt.GridBagLayout; import java.awt.GridBagLayout;
import java.awt.Insets; import java.awt.Insets;
@ -38,8 +37,10 @@ import javax.swing.Box;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerDefaults;
import org.sleuthkit.autopsy.contentviewers.osaccount.SectionData.RowData; import org.sleuthkit.autopsy.contentviewers.osaccount.SectionData.RowData;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
@ -61,7 +62,11 @@ public class OsAccountDataPanel extends JPanel {
private static final int KEY_COLUMN = 0; private static final int KEY_COLUMN = 0;
private static final int VALUE_COLUMN = 1; private static final int VALUE_COLUMN = 1;
private final static Insets FIRST_HEADER_INSETS = new Insets(0, 0, 0, 0);
private final static Insets HEADER_INSETS = new Insets(ContentViewerDefaults.getSectionSpacing(), 0, ContentViewerDefaults.getLineSpacing(), 0);
private final static Insets VALUE_COLUMN_INSETS = new Insets(0, ContentViewerDefaults.getColumnSpacing(), ContentViewerDefaults.getLineSpacing(), 0);
private final static Insets KEY_COLUMN_INSETS = new Insets(0, ContentViewerDefaults.getSectionIndent(), ContentViewerDefaults.getLineSpacing(), 0);
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MMM dd yyyy", US); private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MMM dd yyyy", US);
private PanelDataFetcher dataFetcher = null; private PanelDataFetcher dataFetcher = null;
@ -76,6 +81,7 @@ public class OsAccountDataPanel extends JPanel {
*/ */
private void initialize() { private void initialize() {
this.setLayout(new GridBagLayout()); this.setLayout(new GridBagLayout());
this.setBorder(new EmptyBorder(ContentViewerDefaults.getPanelInsets()));
} }
/** /**
@ -269,7 +275,7 @@ public class OsAccountDataPanel extends JPanel {
private void addTitle(String title, int row) { private void addTitle(String title, int row) {
JLabel label = new JLabel(title); JLabel label = new JLabel(title);
// Make the title bold. // Make the title bold.
label.setFont(label.getFont().deriveFont(Font.BOLD)); label.setFont(ContentViewerDefaults.getHeaderFont());
add(label, getTitleContraints(row)); add(label, getTitleContraints(row));
} }
@ -312,7 +318,9 @@ public class OsAccountDataPanel extends JPanel {
constraints.anchor = GridBagConstraints.NORTHWEST; constraints.anchor = GridBagConstraints.NORTHWEST;
constraints.fill = GridBagConstraints.HORIZONTAL; constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.weightx = 1; constraints.weightx = 1;
constraints.insets = new Insets(5, 5, 5, 9); constraints.insets = (row == 0)
? FIRST_HEADER_INSETS
: HEADER_INSETS;
return constraints; return constraints;
} }
@ -332,7 +340,7 @@ public class OsAccountDataPanel extends JPanel {
constraints.gridwidth = 1; // The title goes across the other columns constraints.gridwidth = 1; // The title goes across the other columns
constraints.gridheight = 1; constraints.gridheight = 1;
constraints.anchor = GridBagConstraints.WEST; constraints.anchor = GridBagConstraints.WEST;
constraints.insets = new Insets(0, 13, 5, 5); constraints.insets = KEY_COLUMN_INSETS;
return constraints; return constraints;
} }
@ -352,8 +360,8 @@ public class OsAccountDataPanel extends JPanel {
constraints.gridwidth = 1; // The title goes across the other columns constraints.gridwidth = 1; // The title goes across the other columns
constraints.gridheight = 1; constraints.gridheight = 1;
constraints.fill = GridBagConstraints.HORIZONTAL; constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.insets = VALUE_COLUMN_INSETS;
constraints.weightx = 1; constraints.weightx = 1;
constraints.insets = new Insets(0, 5, 5, 5);
return constraints; return constraints;
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.corecomponents; package org.sleuthkit.autopsy.corecomponents;
import java.text.MessageFormat;
import javax.swing.JTextPane; import javax.swing.JTextPane;
import javax.swing.SizeRequirements; import javax.swing.SizeRequirements;
import javax.swing.text.Element; import javax.swing.text.Element;
@ -27,6 +28,8 @@ import javax.swing.text.ViewFactory;
import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.InlineView; import javax.swing.text.html.InlineView;
import javax.swing.text.html.ParagraphView; import javax.swing.text.html.ParagraphView;
import javax.swing.text.html.StyleSheet;
import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerDefaults;
import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.EscapeUtil;
/** /**
@ -96,9 +99,15 @@ public class AutoWrappingJTextPane extends JTextPane {
this.setEditorKit(editorKit); this.setEditorKit(editorKit);
} }
@Override @Override
public void setText(String text) { public void setText(String text) {
super.setText("<pre>" + EscapeUtil.escapeHtml(text) + "</pre>"); // setting the text format with style to avoid problems with overridden styles.
String style = String.format("font-family: %s; font-size: %dpt; margin: 0px; padding: 0px 0px %dpx 0px;",
ContentViewerDefaults.getFont().getFamily(), ContentViewerDefaults.getFont().getSize(), ContentViewerDefaults.getLineSpacing());
super.setText(MessageFormat.format("<pre style=\"{0}\">{1}</pre>", style, EscapeUtil.escapeHtml(text)));
} }
} }

View File

@ -21,8 +21,6 @@ package org.sleuthkit.autopsy.corecomponents;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.awt.Component; import java.awt.Component;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.dnd.DnDConstants; import java.awt.dnd.DnDConstants;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
@ -49,6 +47,7 @@ import javax.swing.JTable;
import javax.swing.ListSelectionModel; import javax.swing.ListSelectionModel;
import static javax.swing.SwingConstants.CENTER; import static javax.swing.SwingConstants.CENTER;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent; import javax.swing.event.TableColumnModelEvent;
@ -106,6 +105,27 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(DataResultViewerTable.class.getName()); private static final Logger LOGGER = Logger.getLogger(DataResultViewerTable.class.getName());
// How many rows to sample in order to determine column width.
private static final int SAMPLE_ROW_NUM = 100;
// The padding to be added in addition to content size when considering column width.
private static final int COLUMN_PADDING = 15;
// The minimum column width.
private static final int MIN_COLUMN_WIDTH = 30;
// The maximum column width.
private static final int MAX_COLUMN_WIDTH = 300;
// The minimum row height to use when calculating whether scroll bar will be used.
private static final int MIN_ROW_HEIGHT = 10;
// The width of the scroll bar.
private static final int SCROLL_BAR_WIDTH = ((Integer) UIManager.get("ScrollBar.width")).intValue();
// Any additional padding to be used for the first column.
private static final int FIRST_COL_ADDITIONAL_WIDTH = 0;
private static final String NOTEPAD_ICON_PATH = "org/sleuthkit/autopsy/images/notepad16.png"; private static final String NOTEPAD_ICON_PATH = "org/sleuthkit/autopsy/images/notepad16.png";
private static final String RED_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/red-circle-exclamation.png"; private static final String RED_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/red-circle-exclamation.png";
private static final String YELLOW_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/yellow-circle-yield.png"; private static final String YELLOW_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/yellow-circle-yield.png";
@ -152,7 +172,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
* OutlineView to the actions global context. * OutlineView to the actions global context.
* *
* @param explorerManager The explorer manager of the ancestor top * @param explorerManager The explorer manager of the ancestor top
* component. * component.
*/ */
public DataResultViewerTable(ExplorerManager explorerManager) { public DataResultViewerTable(ExplorerManager explorerManager) {
this(explorerManager, Bundle.DataResultViewerTable_title()); this(explorerManager, Bundle.DataResultViewerTable_title());
@ -165,8 +185,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
* in the OutlineView to the actions global context. * in the OutlineView to the actions global context.
* *
* @param explorerManager The explorer manager of the ancestor top * @param explorerManager The explorer manager of the ancestor top
* component. * component.
* @param title The title. * @param title The title.
*/ */
public DataResultViewerTable(ExplorerManager explorerManager, String title) { public DataResultViewerTable(ExplorerManager explorerManager, String title) {
super(explorerManager); super(explorerManager);
@ -364,6 +384,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
*/ */
if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) { if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) {
this.getExplorerManager().setRootContext(this.rootNode); this.getExplorerManager().setRootContext(this.rootNode);
outline.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
setupTable(); setupTable();
} else { } else {
Node emptyNode = new AbstractNode(Children.LEAF); Node emptyNode = new AbstractNode(Children.LEAF);
@ -416,13 +437,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
firstProp = props.remove(0); firstProp = props.remove(0);
} }
/*
* show the horizontal scroll panel and show all the content & header If
* there is only one column (which was removed from props above) Just
* let the table resize itself.
*/
outline.setAutoResizeMode((props.isEmpty()) ? JTable.AUTO_RESIZE_ALL_COLUMNS : JTable.AUTO_RESIZE_OFF);
assignColumns(props); // assign columns to match the properties assignColumns(props); // assign columns to match the properties
if (firstProp != null) { if (firstProp != null) {
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(firstProp.getDisplayName()); ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(firstProp.getDisplayName());
@ -507,87 +521,59 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
/* /*
* Sets the column widths for the child OutlineView of this tabular results * Sets the column widths for the child OutlineView of this tabular results
* viewer. * viewer providing any additional width to last column.
*/ */
protected void setColumnWidths() { protected void setColumnWidths() {
if (rootNode.getChildren().getNodesCount() != 0) { // based on https://stackoverflow.com/questions/17627431/auto-resizing-the-jtable-column-widths
final Graphics graphics = outlineView.getGraphics(); final TableColumnModel columnModel = outline.getColumnModel();
if (graphics != null) { // the remaining table width that can be used in last row
// Current width of the outlineView double availableTableWidth = outlineView.getSize().getWidth();
double outlineViewWidth = outlineView.getSize().getWidth();
// List of the column widths
List<Integer> columnWidths = new ArrayList<>();
final FontMetrics metrics = graphics.getFontMetrics();
int margin = 4; for (int columnIdx = 0; columnIdx < outline.getColumnCount(); columnIdx++) {
int padding = 8; int columnPadding = (columnIdx == 0) ? FIRST_COL_ADDITIONAL_WIDTH + COLUMN_PADDING : COLUMN_PADDING;
TableColumn tableColumn = columnModel.getColumn(columnIdx);
int totalColumnWidth = 0; // The width of this column
int cntMaxSizeColumns = 0; int width = MIN_COLUMN_WIDTH;
// Calulate the width for each column keeping track of the number // get header cell width
// of columns that were set to columnwidthLimit. // taken in part from https://stackoverflow.com/a/18381924
for (int column = 0; column < outline.getModel().getColumnCount(); column++) { TableCellRenderer headerRenderer = tableColumn.getHeaderRenderer();
int firstColumnPadding = (column == 0) ? 32 : 0; if (headerRenderer == null) {
int columnWidthLimit = (column == 0) ? 350 : 300; headerRenderer = outline.getTableHeader().getDefaultRenderer();
int valuesWidth = 0; }
Object headerValue = tableColumn.getHeaderValue();
// find the maximum width needed to fit the values for the first 100 rows, at most Component headerComp = headerRenderer.getTableCellRendererComponent(outline, headerValue, false, false, 0, columnIdx);
for (int row = 0; row < Math.min(100, outline.getRowCount()); row++) { width = Math.max(headerComp.getPreferredSize().width + columnPadding, width);
TableCellRenderer renderer = outline.getCellRenderer(row, column);
Component comp = outline.prepareRenderer(renderer, row, column); // get the max of row widths from the first SAMPLE_ROW_NUM rows
valuesWidth = Math.max(comp.getPreferredSize().width, valuesWidth); Component comp = null;
} int rowCount = outline.getRowCount();
for (int row = 0; row < Math.min(rowCount, SAMPLE_ROW_NUM); row++) {
int headerWidth = metrics.stringWidth(outline.getColumnName(column)); TableCellRenderer renderer = outline.getCellRenderer(row, columnIdx);
valuesWidth += firstColumnPadding; // add extra padding for first column comp = outline.prepareRenderer(renderer, row, columnIdx);
width = Math.max(comp.getPreferredSize().width + columnPadding, width);
int columnWidth = Math.max(valuesWidth, headerWidth); }
columnWidth += 2 * margin + padding; // add margin and regular padding
// no higher than maximum column width
columnWidth = Math.min(columnWidth, columnWidthLimit); if (width > MAX_COLUMN_WIDTH) {
columnWidths.add(columnWidth); width = MAX_COLUMN_WIDTH;
}
totalColumnWidth += columnWidth;
// if last column, calculate remaining width factoring in the possibility of a scroll bar.
if (columnWidth == columnWidthLimit) { if (columnIdx == outline.getColumnCount() - 1) {
cntMaxSizeColumns++; int rowHeight = comp == null ? MIN_ROW_HEIGHT : comp.getPreferredSize().height;
} if (headerComp.getPreferredSize().height + rowCount * rowHeight > outlineView.getSize().getHeight()) {
} availableTableWidth -= SCROLL_BAR_WIDTH;
}
// Figure out how much extra, if any can be given to the columns
// so that the table is as wide as outlineViewWidth. If cntMaxSizeColumns columnModel.getColumn(columnIdx).setPreferredWidth(Math.max(width, (int) availableTableWidth));
// is greater than 0 divide the extra space between the columns } else {
// that could use more space. Otherwise divide evenly amoung // otherwise set preferred width to width and decrement availableTableWidth accordingly
// all columns. columnModel.getColumn(columnIdx).setPreferredWidth(width);
int extraWidth = 0; availableTableWidth -= width;
if (totalColumnWidth < outlineViewWidth) {
if (cntMaxSizeColumns > 0) {
extraWidth = (int) ((outlineViewWidth - totalColumnWidth) / cntMaxSizeColumns);
} else {
extraWidth = (int) ((outlineViewWidth - totalColumnWidth) / columnWidths.size());
}
}
for (int column = 0; column < columnWidths.size(); column++) {
int columnWidth = columnWidths.get(column);
if (cntMaxSizeColumns > 0) {
if (columnWidth >= ((column == 0) ? 350 : 300)) {
columnWidth += extraWidth;
}
} else {
columnWidth += extraWidth;
}
outline.getColumnModel().getColumn(column).setPreferredWidth(columnWidth);
}
} }
} else {
// if there's no content just auto resize all columns
outline.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
} }
} }
@ -743,7 +729,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
* order. * order.
* *
* @return a List<Node.Property<?>> of the properties in the persisted * @return a List<Node.Property<?>> of the properties in the persisted
* order. * order.
*/ */
private synchronized List<Node.Property<?>> loadColumnOrder() { private synchronized List<Node.Property<?>> loadColumnOrder() {
@ -1260,18 +1246,20 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
/** /**
* Returns the icon denoted by the Score's Significance. * Returns the icon denoted by the Score's Significance.
*
* @param significance The Score's Significance. * @param significance The Score's Significance.
*
* @return The icon (or null) related to that significance. * @return The icon (or null) related to that significance.
*/ */
private ImageIcon getIcon(Significance significance) { private ImageIcon getIcon(Significance significance) {
if (significance == null) { if (significance == null) {
return null; return null;
} }
switch (significance) { switch (significance) {
case NOTABLE: case NOTABLE:
return NOTABLE_ICON_SCORE; return NOTABLE_ICON_SCORE;
case LIKELY_NOTABLE: case LIKELY_NOTABLE:
return INTERESTING_SCORE_ICON; return INTERESTING_SCORE_ICON;
case LIKELY_NONE: case LIKELY_NONE:
case NONE: case NONE:
@ -1280,7 +1268,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
return null; return null;
} }
} }
@Override @Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2020 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -36,6 +36,7 @@ import org.openide.nodes.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -277,6 +278,8 @@ public class Artifacts {
*/ */
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this); private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
private final Category category; private final Category category;
private final PropertyChangeListener weakPcl;
/** /**
* Main constructor. * Main constructor.
@ -290,45 +293,50 @@ public class Artifacts {
super(); super();
this.filteringDSObjId = filteringDSObjId; this.filteringDSObjId = filteringDSObjId;
this.category = category; this.category = category;
}
PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { String eventType = evt.getPropertyName();
String eventType = evt.getPropertyName(); if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { // case was closed. Remove listeners so that we don't get called with a stale case handle
// case was closed. Remove listeners so that we don't get called with a stale case handle if (evt.getNewValue() == null) {
if (evt.getNewValue() == null) { removeNotify();
removeNotify(); }
} } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
} else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
/**
* This is a stop gap measure until a different way of handling
* the closing of cases is worked out. Currently, remote events
* may be received for a case that is already closed.
*/
try {
Case.getCurrentCaseThrows();
refresh(false);
} catch (NoCurrentCaseException notUsed) {
/** /**
* Case is closed, do nothing. * This is a stop gap measure until a different way of
* handling the closing of cases is worked out. Currently,
* remote events may be received for a case that is already
* closed.
*/ */
try {
Case.getCurrentCaseThrows();
refresh(false);
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
} }
} };
};
weakPcl = WeakListeners.propertyChange(pcl, null);
}
@Override @Override
protected void addNotify() { protected void addNotify() {
super.addNotify();
refreshThrottler.registerForIngestModuleEvents(); refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable {
super.finalize();
refreshThrottler.unregisterEventListener(); refreshThrottler.unregisterEventListener();
IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
typeNodeMap.clear(); typeNodeMap.clear();
} }
@ -624,17 +632,21 @@ public class Artifacts {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
@Override @Override
protected void onAdd() { protected void onAdd() {
refreshThrottler.registerForIngestModuleEvents(); refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
} }
@Override @Override
protected void onRemove() { protected void onRemove() {
refreshThrottler.unregisterEventListener(); if(refreshThrottler != null) {
IngestManager.getInstance().removeIngestJobEventListener(pcl); refreshThrottler.unregisterEventListener();
}
IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
} }
@Override @Override

View File

@ -1,6 +1,5 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
*
* Copyright 2018-2021 Basis Technology Corp. * Copyright 2018-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
@ -20,7 +19,6 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
@ -32,6 +30,7 @@ import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.openide.nodes.ChildFactory; import org.openide.nodes.ChildFactory;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.WeakListeners;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -83,16 +82,18 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
@Override @Override
protected void addNotify() { protected void addNotify() {
super.addNotify(); super.addNotify();
Case.addEventTypeSubscriber(EVENTS_OF_INTEREST, pcl); Case.addEventTypeSubscriber(EVENTS_OF_INTEREST, weakPcl);
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable {
super.removeNotify(); super.finalize();
Case.removeEventTypeSubscriber(EVENTS_OF_INTEREST, pcl); Case.removeEventTypeSubscriber(EVENTS_OF_INTEREST, weakPcl);
} }
/** /**

View File

@ -115,14 +115,15 @@ public abstract class BaseChildFactory<T extends Content> extends ChildFactory.D
isPageSizeChangeEvent = false; isPageSizeChangeEvent = false;
this.filter = filter; this.filter = filter;
} }
@Override @Override
protected void addNotify() { protected void addNotify() {
onAdd(); onAdd();
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable {
super.finalize();
onRemove(); onRemove();
} }

View File

@ -103,7 +103,21 @@ class ContentChildren extends AbstractContentChildren<Content> {
@Override @Override
protected List<Content> makeKeys() { protected List<Content> makeKeys() {
return getDisplayChildren(parent); List<Content> contentList = getDisplayChildren(parent);
// Call the getUniquePath method to cache the value for future use
// in the EDT
contentList.forEach(content->{
try {
content.getUniquePath();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Failed attempt to cache the "
+ "unique path of the abstract file instance. Name: %s (objID=%d)",
content.getName(), content.getId()), ex);
}
});
return contentList;
} }
@Override @Override

View File

@ -31,6 +31,7 @@ import org.openide.nodes.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -76,15 +77,18 @@ public class DataSourcesNode extends DisplayableItemNode {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
@Override @Override
protected void addNotify() { protected void addNotify() {
Case.addEventTypeSubscriber(UPDATE_EVTS, pcl); Case.addEventTypeSubscriber(UPDATE_EVTS, weakPcl);
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable{
Case.removeEventTypeSubscriber(UPDATE_EVTS, pcl); Case.removeEventTypeSubscriber(UPDATE_EVTS, weakPcl);
super.finalize();
} }
@Override @Override

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2012-2020 Basis Technology Corp. * Copyright 2012-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -37,6 +37,7 @@ import org.openide.nodes.Children;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -313,21 +314,24 @@ public class EmailExtracted implements AutopsyVisitableItem {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
@Override @Override
protected void addNotify() { protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
emailResults.update(); emailResults.update();
emailResults.addObserver(this); emailResults.addObserver(this);
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable{
IngestManager.getInstance().removeIngestJobEventListener(pcl); super.finalize();
IngestManager.getInstance().removeIngestModuleEventListener(pcl); IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); IngestManager.getInstance().removeIngestModuleEventListener(weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
emailResults.deleteObserver(this); emailResults.deleteObserver(this);
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2020 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -39,6 +39,7 @@ import org.openide.nodes.Children;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -278,21 +279,24 @@ public class HashsetHits implements AutopsyVisitableItem {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
@Override @Override
protected void addNotify() { protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
hashsetResults.update(); hashsetResults.update();
hashsetResults.addObserver(this); hashsetResults.addObserver(this);
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable {
IngestManager.getInstance().removeIngestJobEventListener(pcl); super.finalize();
IngestManager.getInstance().removeIngestModuleEventListener(pcl); IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); IngestManager.getInstance().removeIngestModuleEventListener(weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
hashsetResults.deleteObserver(this); hashsetResults.deleteObserver(this);
} }

View File

@ -66,7 +66,7 @@ public class HostNode extends DisplayableItemNode {
private final Host host; private final Host host;
private final Function<DataSourceGrouping, Node> dataSourceToNode; private final Function<DataSourceGrouping, Node> dataSourceToNode;
/** /**
* Main constructor. * Main constructor.
* *
@ -77,7 +77,7 @@ public class HostNode extends DisplayableItemNode {
this.host = host; this.host = host;
this.dataSourceToNode = dataSourceToNode; this.dataSourceToNode = dataSourceToNode;
} }
/** /**
* Listener for handling DATA_SOURCE_ADDED / HOST_DELETED events. * Listener for handling DATA_SOURCE_ADDED / HOST_DELETED events.
* A host may have been deleted as part of a merge, which means its data sources could * A host may have been deleted as part of a merge, which means its data sources could
@ -93,15 +93,19 @@ public class HostNode extends DisplayableItemNode {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(dataSourceAddedPcl, null);
@Override @Override
protected void addNotify() { protected void addNotify() {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_DELETED), dataSourceAddedPcl); super.addNotify();
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_DELETED), weakPcl);
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable {
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_DELETED), dataSourceAddedPcl); super.finalize();
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_DELETED), weakPcl);
} }
@Override @Override
@ -222,7 +226,7 @@ public class HostNode extends DisplayableItemNode {
* @param hostGrouping The HostGrouping key. * @param hostGrouping The HostGrouping key.
*/ */
HostNode(HostGrouping hostGrouping) { HostNode(HostGrouping hostGrouping) {
this(Children.create(new HostGroupingChildren(HOST_GROUPING_CONVERTER, hostGrouping.getHost()), true), hostGrouping.getHost()); this(Children.create(new HostGroupingChildren(HOST_GROUPING_CONVERTER, hostGrouping.getHost()), false), hostGrouping.getHost());
} }
/** /**

View File

@ -20,8 +20,6 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
@ -35,7 +33,6 @@ import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.DeleteDataSourceAction; import org.sleuthkit.autopsy.casemodule.DeleteDataSourceAction;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.datasourcesummary.ui.ViewSummaryInformationAction; import org.sleuthkit.autopsy.datasourcesummary.ui.ViewSummaryInformationAction;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
@ -48,7 +45,6 @@ import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.RunIngestModulesAction; import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.RunIngestModulesAction;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.VirtualDirectory; import org.sleuthkit.datamodel.VirtualDirectory;
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException; import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException;
@ -171,17 +167,10 @@ public class ImageNode extends AbstractContentNode<Image> {
Bundle.ImageNode_createSheet_timezone_desc(), Bundle.ImageNode_createSheet_timezone_desc(),
this.content.getTimeZone())); this.content.getTimeZone()));
try (CaseDbQuery query = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery("SELECT device_id FROM data_source_info WHERE obj_id = " + this.content.getId());) { sheetSet.put(new NodeProperty<>(Bundle.ImageNode_createSheet_deviceId_name(),
ResultSet deviceIdSet = query.getResultSet();
if (deviceIdSet.next()) {
sheetSet.put(new NodeProperty<>(Bundle.ImageNode_createSheet_deviceId_name(),
Bundle.ImageNode_createSheet_deviceId_displayName(), Bundle.ImageNode_createSheet_deviceId_displayName(),
Bundle.ImageNode_createSheet_deviceId_desc(), Bundle.ImageNode_createSheet_deviceId_desc(),
deviceIdSet.getString("device_id"))); content.getDeviceId()));
}
} catch (SQLException | TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Failed to get device id for the following image: " + this.content.getId(), ex);
}
return sheet; return sheet;
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2020 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -39,6 +39,7 @@ import org.openide.nodes.Children;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -271,21 +272,24 @@ public class InterestingHits implements AutopsyVisitableItem {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
@Override @Override
protected void addNotify() { protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
interestingResults.update(); interestingResults.update();
interestingResults.addObserver(this); interestingResults.addObserver(this);
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable {
IngestManager.getInstance().removeIngestJobEventListener(pcl); super.finalize();
IngestManager.getInstance().removeIngestModuleEventListener(pcl); IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); IngestManager.getInstance().removeIngestModuleEventListener(weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
interestingResults.deleteObserver(this); interestingResults.deleteObserver(this);
} }

View File

@ -42,6 +42,7 @@ import org.openide.nodes.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -437,7 +438,8 @@ public class KeywordHits implements AutopsyVisitableItem {
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable {
super.finalize();
keywordResults.deleteObserver(this); keywordResults.deleteObserver(this);
} }
@ -504,22 +506,24 @@ public class KeywordHits implements AutopsyVisitableItem {
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
@Override @Override
protected void addNotify() { protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
keywordResults.update(); keywordResults.update();
super.addNotify(); super.addNotify();
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable{
IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
super.removeNotify(); super.finalize();
} }
@Override @Override

View File

@ -137,19 +137,22 @@ public final class OsAccounts implements AutopsyVisitableItem {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(listener, null);
@Override
protected void finalize() throws Throwable {
super.finalize();
Case.removeEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNTS_ADDED), weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
}
@Override @Override
protected void addNotify() { protected void addNotify() {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.OS_ACCOUNTS_ADDED, Case.Events.OS_ACCOUNTS_DELETED), listener); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.OS_ACCOUNTS_ADDED, Case.Events.OS_ACCOUNTS_DELETED), listener);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener);
} }
@Override
protected void removeNotify() {
Case.removeEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNTS_ADDED), listener);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener);
}
@Override @Override
protected boolean createKeys(List<OsAccount> list) { protected boolean createKeys(List<OsAccount> list) {
if (skCase != null) { if (skCase != null) {

View File

@ -92,6 +92,7 @@ public class PersonNode extends DisplayableItemNode {
*/ */
PersonChildren(Person person) { PersonChildren(Person person) {
this.person = person; this.person = person;
} }
/** /**
@ -111,17 +112,20 @@ public class PersonNode extends DisplayableItemNode {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(hostAddedDeletedPcl, null);
@Override @Override
protected void addNotify() { protected void addNotify() {
Case.addEventTypeSubscriber(HOST_EVENTS_OF_INTEREST, hostAddedDeletedPcl); Case.addEventTypeSubscriber(HOST_EVENTS_OF_INTEREST, weakPcl);
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable {
Case.removeEventTypeSubscriber(HOST_EVENTS_OF_INTEREST, hostAddedDeletedPcl); super.finalize();
Case.removeEventTypeSubscriber(HOST_EVENTS_OF_INTEREST, weakPcl);
} }
@Override @Override
protected HostNode createNodeForKey(HostGrouping key) { protected HostNode createNodeForKey(HostGrouping key) {
return key == null ? null : new HostNode(key); return key == null ? null : new HostNode(key);

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2019 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -33,6 +33,7 @@ import org.openide.nodes.Children;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -213,6 +214,8 @@ public class Tags implements AutopsyVisitableItem {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
/** /**
* Constructor * Constructor
@ -221,21 +224,21 @@ public class Tags implements AutopsyVisitableItem {
*/ */
TagNameNodeFactory(long objId) { TagNameNodeFactory(long objId) {
this.filteringDSObjId = objId; this.filteringDSObjId = objId;
} }
@Override @Override
protected void addNotify() { protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
tagResults.update(); tagResults.update();
tagResults.addObserver(this); tagResults.addObserver(this);
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable {
IngestManager.getInstance().removeIngestJobEventListener(pcl); super.finalize();
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
tagResults.deleteObserver(this); tagResults.deleteObserver(this);
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2019 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -57,6 +57,7 @@ import org.openide.nodes.NodeOp;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.Utilities; import org.openide.util.Utilities;
import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -218,8 +219,8 @@ final public class Accounts implements AutopsyVisitableItem {
abstract void handleDataAdded(ModuleDataEvent event); abstract void handleDataAdded(ModuleDataEvent event);
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable {
super.removeNotify(); super.finalize();
reviewStatusBus.unregister(ObservingChildren.this); reviewStatusBus.unregister(ObservingChildren.this);
} }
@ -415,6 +416,8 @@ final public class Accounts implements AutopsyVisitableItem {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
@Subscribe @Subscribe
@Override @Override
@ -473,18 +476,18 @@ final public class Accounts implements AutopsyVisitableItem {
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable {
IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
super.removeNotify(); super.finalize();
} }
@Override @Override
protected void addNotify() { protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
super.addNotify(); super.addNotify();
refresh(true); refresh(true);
} }
@ -550,21 +553,22 @@ final public class Accounts implements AutopsyVisitableItem {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
@Override @Override
protected void addNotify() { protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
super.addNotify();
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable {
IngestManager.getInstance().removeIngestJobEventListener(pcl); super.finalize();
IngestManager.getInstance().removeIngestModuleEventListener(pcl); IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); IngestManager.getInstance().removeIngestModuleEventListener(weakPcl);
super.removeNotify(); Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
} }
@Override @Override
@ -727,6 +731,9 @@ final public class Accounts implements AutopsyVisitableItem {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
@Subscribe @Subscribe
@Override @Override
@ -739,20 +746,21 @@ final public class Accounts implements AutopsyVisitableItem {
void handleDataAdded(ModuleDataEvent event) { void handleDataAdded(ModuleDataEvent event) {
refresh(true); refresh(true);
} }
@Override @Override
protected void addNotify() { protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
super.addNotify(); super.addNotify();
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable {
IngestManager.getInstance().removeIngestJobEventListener(pcl); super.finalize();
IngestManager.getInstance().removeIngestModuleEventListener(pcl); IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); IngestManager.getInstance().removeIngestModuleEventListener(weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
super.removeNotify(); super.removeNotify();
} }
@ -881,21 +889,23 @@ final public class Accounts implements AutopsyVisitableItem {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
@Override @Override
protected void addNotify() { protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
super.addNotify(); super.addNotify();
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable {
IngestManager.getInstance().removeIngestJobEventListener(pcl); super.finalize();
IngestManager.getInstance().removeIngestModuleEventListener(pcl); IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); IngestManager.getInstance().removeIngestModuleEventListener(weakPcl);
super.removeNotify(); Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
} }
@Subscribe @Subscribe
@ -1095,21 +1105,23 @@ final public class Accounts implements AutopsyVisitableItem {
} }
} }
}; };
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
@Override @Override
protected void addNotify() { protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl);
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
super.addNotify(); super.addNotify();
} }
@Override @Override
protected void removeNotify() { protected void finalize() throws Throwable{
IngestManager.getInstance().removeIngestJobEventListener(pcl); super.finalize();
IngestManager.getInstance().removeIngestModuleEventListener(pcl); IngestManager.getInstance().removeIngestJobEventListener(weakPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); IngestManager.getInstance().removeIngestModuleEventListener(weakPcl);
super.removeNotify(); Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl);
} }
@Subscribe @Subscribe

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2015-2019 Basis Technology Corp. * Copyright 2015-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.keywordsearchservice; package org.sleuthkit.autopsy.keywordsearchservice;
import com.google.common.annotations.Beta;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import org.sleuthkit.autopsy.casemodule.CaseMetadata; import org.sleuthkit.autopsy.casemodule.CaseMetadata;
@ -105,5 +106,14 @@ public interface KeywordSearchService extends Closeable {
* @throws KeywordSearchServiceException if unable to delete. * @throws KeywordSearchServiceException if unable to delete.
*/ */
void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException; void deleteDataSource(Long dataSourceId) throws KeywordSearchServiceException;
/**
* A flag to enable or disable OCR on all future text indexing.
*
* @param state Boolean flag to enable/disable OCR. Set to True to enable
* OCR, or False to disable it.
*/
@Beta
void changeOcrState(boolean state);
} }

View File

@ -46,6 +46,7 @@ import net.sf.sevenzipjbinding.PropID;
import net.sf.sevenzipjbinding.SevenZip; import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.SevenZipException; import net.sf.sevenzipjbinding.SevenZipException;
import net.sf.sevenzipjbinding.SevenZipNativeInitializationException; import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
import org.apache.tika.Tika;
import org.apache.tika.parser.txt.CharsetDetector; import org.apache.tika.parser.txt.CharsetDetector;
import org.apache.tika.parser.txt.CharsetMatch; import org.apache.tika.parser.txt.CharsetMatch;
import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandle;
@ -194,6 +195,15 @@ class SevenZipExtractor {
} }
return false; return false;
} }
boolean isSevenZipExtractionSupported(String mimeType) {
for (SupportedArchiveExtractionFormats supportedMimeType : SupportedArchiveExtractionFormats.values()) {
if (mimeType.contains(supportedMimeType.toString())) {
return true;
}
}
return false;
}
/** /**
* Private helper method to standardize the cancellation check that is * Private helper method to standardize the cancellation check that is
@ -789,33 +799,18 @@ class SevenZipExtractor {
// add them to the DB. We wait until the end so that we have the metadata on all of the // add them to the DB. We wait until the end so that we have the metadata on all of the
// intermediate nodes since the order is not guaranteed // intermediate nodes since the order is not guaranteed
try { try {
unpackedTree.updateOrAddFileToCaseRec(statusMap, archiveFilePath); unpackedTree.updateOrAddFileToCaseRec(statusMap, archiveFilePath, parentAr, archiveFile, depthMap);
if (checkForIngestCancellation(archiveFile)) { if (checkForIngestCancellation(archiveFile)) {
return false; return false;
} }
unpackedFiles = unpackedTree.getAllFileObjects();
//check if children are archives, update archive depth tracking
for (int i = 0; i < unpackedFiles.size(); i++) {
if (checkForIngestCancellation(archiveFile)) {
return false;
}
progress.progress(String.format("%s: Searching for nested archives (%d of %d)", currentArchiveName, i + 1, unpackedFiles.size()));
AbstractFile unpackedFile = unpackedFiles.get(i);
if (unpackedFile == null) {
continue;
}
if (isSevenZipExtractionSupported(unpackedFile)) {
Archive child = new Archive(parentAr.getDepth() + 1, parentAr.getRootArchiveId(), archiveFile);
parentAr.addChild(child);
depthMap.put(unpackedFile.getId(), child);
}
unpackedFile.close();
}
} catch (TskCoreException | NoCurrentCaseException e) { } catch (TskCoreException | NoCurrentCaseException e) {
logger.log(Level.SEVERE, "Error populating complete derived file hierarchy from the unpacked dir structure", e); //NON-NLS logger.log(Level.SEVERE, "Error populating complete derived file hierarchy from the unpacked dir structure", e); //NON-NLS
//TODO decide if anything to cleanup, for now bailing //TODO decide if anything to cleanup, for now bailing
} }
// Get the new files to be added to the case.
unpackedFiles = unpackedTree.getAllFileObjects();
} catch (SevenZipException | IllegalArgumentException ex) { } catch (SevenZipException | IllegalArgumentException ex) {
logger.log(Level.WARNING, "Error unpacking file: " + archiveFile, ex); //NON-NLS logger.log(Level.WARNING, "Error unpacking file: " + archiveFile, ex); //NON-NLS
@ -991,6 +986,8 @@ class SevenZipExtractor {
private EncodedFileOutputStream output; private EncodedFileOutputStream output;
private String localAbsPath; private String localAbsPath;
private int bytesWritten; private int bytesWritten;
private static final Tika tika = new Tika();
private String mimeType = "";
UnpackStream(String localAbsPath) throws IOException { UnpackStream(String localAbsPath) throws IOException {
this.output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1); this.output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
@ -1003,6 +1000,7 @@ class SevenZipExtractor {
this.output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1); this.output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
this.localAbsPath = localAbsPath; this.localAbsPath = localAbsPath;
this.bytesWritten = 0; this.bytesWritten = 0;
this.mimeType = "";
} }
public int getSize() { public int getSize() {
@ -1012,6 +1010,10 @@ class SevenZipExtractor {
@Override @Override
public int write(byte[] bytes) throws SevenZipException { public int write(byte[] bytes) throws SevenZipException {
try { try {
// Detect MIME type now while the file is in memory
if (bytesWritten == 0) {
mimeType = tika.detect(bytes);
}
output.write(bytes); output.write(bytes);
this.bytesWritten += bytes.length; this.bytesWritten += bytes.length;
} catch (IOException ex) { } catch (IOException ex) {
@ -1023,6 +1025,10 @@ class SevenZipExtractor {
return bytes.length; return bytes.length;
} }
public String getMIMEType() {
return mimeType;
}
public void close() throws IOException { public void close() throws IOException {
try (EncodedFileOutputStream out = output) { try (EncodedFileOutputStream out = output) {
out.flush(); out.flush();
@ -1196,6 +1202,8 @@ class SevenZipExtractor {
0L, createTimeInSeconds, accessTimeInSeconds, modTimeInSeconds, 0L, createTimeInSeconds, accessTimeInSeconds, modTimeInSeconds,
localRelPath); localRelPath);
return; return;
} else {
unpackedNode.setMimeType(unpackStream.getMIMEType());
} }
final String localAbsPath = archiveDetailsMap.get( final String localAbsPath = archiveDetailsMap.get(
@ -1413,10 +1421,10 @@ class SevenZipExtractor {
* Traverse the tree top-down after unzipping is done and create derived * Traverse the tree top-down after unzipping is done and create derived
* files for the entire hierarchy * files for the entire hierarchy
*/ */
void updateOrAddFileToCaseRec(HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath) throws TskCoreException, NoCurrentCaseException { void updateOrAddFileToCaseRec(HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath, Archive parentAr, AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap) throws TskCoreException, NoCurrentCaseException {
final FileManager fileManager = Case.getCurrentCaseThrows().getServices().getFileManager(); final FileManager fileManager = Case.getCurrentCaseThrows().getServices().getFileManager();
for (UnpackedNode child : rootNode.getChildren()) { for (UnpackedNode child : rootNode.getChildren()) {
updateOrAddFileToCaseRec(child, fileManager, statusMap, archiveFilePath); updateOrAddFileToCaseRec(child, fileManager, statusMap, archiveFilePath, parentAr, archiveFile, depthMap);
} }
} }
@ -1434,7 +1442,7 @@ class SevenZipExtractor {
* *
* @throws TskCoreException * @throws TskCoreException
*/ */
private void updateOrAddFileToCaseRec(UnpackedNode node, FileManager fileManager, HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath) throws TskCoreException { private void updateOrAddFileToCaseRec(UnpackedNode node, FileManager fileManager, HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath, Archive parentAr, AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap) throws TskCoreException {
DerivedFile df; DerivedFile df;
progress.progress(String.format("%s: Adding/updating files in case database (%d of %d)", currentArchiveName, ++nodesProcessed, numItems)); progress.progress(String.format("%s: Adding/updating files in case database (%d of %d)", currentArchiveName, ++nodesProcessed, numItems));
try { try {
@ -1497,10 +1505,17 @@ class SevenZipExtractor {
} }
} }
} }
// Check for zip bombs
if (isSevenZipExtractionSupported(node.getMimeType())) {
Archive child = new Archive(parentAr.getDepth() + 1, parentAr.getRootArchiveId(), archiveFile);
parentAr.addChild(child);
depthMap.put(node.getFile().getId(), child);
}
//recurse adding the children if this file was incomplete the children presumably need to be added //recurse adding the children if this file was incomplete the children presumably need to be added
for (UnpackedNode child : node.getChildren()) { for (UnpackedNode child : node.getChildren()) {
updateOrAddFileToCaseRec(child, fileManager, statusMap, getKeyFromUnpackedNode(node, archiveFilePath)); updateOrAddFileToCaseRec(child, fileManager, statusMap, getKeyFromUnpackedNode(node, archiveFilePath), parentAr, archiveFile, depthMap);
} }
} }
@ -1517,6 +1532,7 @@ class SevenZipExtractor {
private long size; private long size;
private long ctime, crtime, atime, mtime; private long ctime, crtime, atime, mtime;
private boolean isFile; private boolean isFile;
private String mimeType = "";
private UnpackedNode parent; private UnpackedNode parent;
//root constructor //root constructor
@ -1593,6 +1609,14 @@ class SevenZipExtractor {
void setFile(AbstractFile file) { void setFile(AbstractFile file) {
this.file = file; this.file = file;
} }
void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
String getMimeType() {
return mimeType;
}
/** /**
* get child by name or null if it doesn't exist * get child by name or null if it doesn't exist

View File

@ -157,6 +157,102 @@ final class AutoIngestAdminActions {
} }
} }
} }
@NbBundle.Messages({"AutoIngestAdminActions.enableOCR.title=Enable OCR For This Case",
"AutoIngestAdminActions.enableOCR.error=Failed to enable OCR for case \"%s\"."})
static final class EnableOCR extends AbstractAction {
private static final long serialVersionUID = 1L;
private final AutoIngestJob job;
EnableOCR(AutoIngestJob job) {
super(Bundle.AutoIngestAdminActions_enableOCR_title());
this.job = job;
}
@Override
public void actionPerformed(ActionEvent e) {
if (job == null) {
return;
}
final AutoIngestDashboardTopComponent tc = (AutoIngestDashboardTopComponent) WindowManager.getDefault().findTopComponent(AutoIngestDashboardTopComponent.PREFERRED_ID);
if (tc == null) {
return;
}
AutoIngestDashboard dashboard = tc.getAutoIngestDashboard();
if (dashboard != null) {
dashboard.getPendingJobsPanel().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
EventQueue.invokeLater(() -> {
try {
dashboard.getMonitor().changeOcrStateForCase(job.getManifest().getCaseName(), true);
dashboard.getPendingJobsPanel().refresh(new AutoIngestNodeRefreshEvents.RefreshCaseEvent(dashboard.getMonitor(), job.getManifest().getCaseName()));
} catch (AutoIngestMonitor.AutoIngestMonitorException ex) {
String errorMessage = String.format(Bundle.AutoIngestAdminActions_enableOCR_error(), job.getManifest().getCaseName());
logger.log(Level.SEVERE, errorMessage, ex);
MessageNotifyUtil.Message.error(errorMessage);
} finally {
dashboard.getPendingJobsPanel().setCursor(Cursor.getDefaultCursor());
}
});
}
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); //To change body of generated methods, choose Tools | Templates.
}
}
@NbBundle.Messages({"AutoIngestAdminActions.disableOCR.title=Disable OCR For This Case",
"AutoIngestAdminActions.disableOCR.error=Failed to disable OCR for case \"%s\"."})
static final class DisableOCR extends AbstractAction {
private static final long serialVersionUID = 1L;
private final AutoIngestJob job;
DisableOCR(AutoIngestJob job) {
super(Bundle.AutoIngestAdminActions_disableOCR_title());
this.job = job;
}
@Override
public void actionPerformed(ActionEvent e) {
if (job == null) {
return;
}
final AutoIngestDashboardTopComponent tc = (AutoIngestDashboardTopComponent) WindowManager.getDefault().findTopComponent(AutoIngestDashboardTopComponent.PREFERRED_ID);
if (tc == null) {
return;
}
AutoIngestDashboard dashboard = tc.getAutoIngestDashboard();
if (dashboard != null) {
dashboard.getPendingJobsPanel().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
EventQueue.invokeLater(() -> {
try {
dashboard.getMonitor().changeOcrStateForCase(job.getManifest().getCaseName(), false);
dashboard.getPendingJobsPanel().refresh(new AutoIngestNodeRefreshEvents.RefreshCaseEvent(dashboard.getMonitor(), job.getManifest().getCaseName()));
} catch (AutoIngestMonitor.AutoIngestMonitorException ex) {
String errorMessage = String.format(Bundle.AutoIngestAdminActions_disableOCR_error(), job.getManifest().getCaseName());
logger.log(Level.SEVERE, errorMessage, ex);
MessageNotifyUtil.Message.error(errorMessage);
} finally {
dashboard.getPendingJobsPanel().setCursor(Cursor.getDefaultCursor());
}
});
}
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); //To change body of generated methods, choose Tools | Templates.
}
}
@NbBundle.Messages({"AutoIngestAdminActions.progressDialogAction.title=Ingest Progress"}) @NbBundle.Messages({"AutoIngestAdminActions.progressDialogAction.title=Ingest Progress"})
static final class ProgressDialogAction extends AbstractAction { static final class ProgressDialogAction extends AbstractAction {

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2015-2018 Basis Technology Corp. * Copyright 2015-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -122,6 +122,8 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
private static final int RUNNING_TABLE_COL_PREFERRED_WIDTH = 175; private static final int RUNNING_TABLE_COL_PREFERRED_WIDTH = 175;
private static final int PRIORITY_COLUMN_PREFERRED_WIDTH = 60; private static final int PRIORITY_COLUMN_PREFERRED_WIDTH = 60;
private static final int PRIORITY_COLUMN_MAX_WIDTH = 150; private static final int PRIORITY_COLUMN_MAX_WIDTH = 150;
private static final int OCR_COLUMN_PREFERRED_WIDTH = 50;
private static final int OCR_COLUMN_MAX_WIDTH = 150;
private static final int ACTIVITY_TIME_COL_MIN_WIDTH = 250; private static final int ACTIVITY_TIME_COL_MIN_WIDTH = 250;
private static final int ACTIVITY_TIME_COL_MAX_WIDTH = 450; private static final int ACTIVITY_TIME_COL_MAX_WIDTH = 450;
private static final int TIME_COL_MIN_WIDTH = 30; private static final int TIME_COL_MIN_WIDTH = 30;
@ -133,9 +135,9 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
private static final int ACTIVITY_COL_MIN_WIDTH = 70; private static final int ACTIVITY_COL_MIN_WIDTH = 70;
private static final int ACTIVITY_COL_MAX_WIDTH = 2000; private static final int ACTIVITY_COL_MAX_WIDTH = 2000;
private static final int ACTIVITY_COL_PREFERRED_WIDTH = 300; private static final int ACTIVITY_COL_PREFERRED_WIDTH = 300;
private static final int STATUS_COL_MIN_WIDTH = 55; private static final int STATUS_COL_MIN_WIDTH = 50;
private static final int STATUS_COL_MAX_WIDTH = 250; private static final int STATUS_COL_MAX_WIDTH = 250;
private static final int STATUS_COL_PREFERRED_WIDTH = 55; private static final int STATUS_COL_PREFERRED_WIDTH = 50;
private static final int COMPLETED_TIME_COL_MIN_WIDTH = 30; private static final int COMPLETED_TIME_COL_MIN_WIDTH = 30;
private static final int COMPLETED_TIME_COL_MAX_WIDTH = 2000; private static final int COMPLETED_TIME_COL_MAX_WIDTH = 2000;
private static final int COMPLETED_TIME_COL_PREFERRED_WIDTH = 280; private static final int COMPLETED_TIME_COL_PREFERRED_WIDTH = 280;
@ -179,7 +181,8 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
"AutoIngestControlPanel.JobsTableModel.ColumnHeader.Status=Status", "AutoIngestControlPanel.JobsTableModel.ColumnHeader.Status=Status",
"AutoIngestControlPanel.JobsTableModel.ColumnHeader.CaseFolder=Case Folder", "AutoIngestControlPanel.JobsTableModel.ColumnHeader.CaseFolder=Case Folder",
"AutoIngestControlPanel.JobsTableModel.ColumnHeader.LocalJob= Local Job?", "AutoIngestControlPanel.JobsTableModel.ColumnHeader.LocalJob= Local Job?",
"AutoIngestControlPanel.JobsTableModel.ColumnHeader.ManifestFilePath= Manifest File Path" "AutoIngestControlPanel.JobsTableModel.ColumnHeader.ManifestFilePath= Manifest File Path",
"AutoIngestControlPanel.JobsTableModel.ColumnHeader.OCR=OCR"
}) })
private enum JobsTableModelColumns { private enum JobsTableModelColumns {
@ -195,7 +198,8 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
CASE_DIRECTORY_PATH(NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.JobsTableModel.ColumnHeader.CaseFolder")), CASE_DIRECTORY_PATH(NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.JobsTableModel.ColumnHeader.CaseFolder")),
IS_LOCAL_JOB(NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.JobsTableModel.ColumnHeader.LocalJob")), IS_LOCAL_JOB(NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.JobsTableModel.ColumnHeader.LocalJob")),
MANIFEST_FILE_PATH(NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.JobsTableModel.ColumnHeader.ManifestFilePath")), MANIFEST_FILE_PATH(NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.JobsTableModel.ColumnHeader.ManifestFilePath")),
PRIORITY(NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.JobsTableModel.ColumnHeader.Priority")); PRIORITY(NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.JobsTableModel.ColumnHeader.Priority")),
OCR(NbBundle.getMessage(AutoIngestControlPanel.class, "AutoIngestControlPanel.JobsTableModel.ColumnHeader.OCR"));
private final String header; private final String header;
private JobsTableModelColumns(String header) { private JobsTableModelColumns(String header) {
@ -219,7 +223,8 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
CASE_DIRECTORY_PATH.getColumnHeader(), CASE_DIRECTORY_PATH.getColumnHeader(),
IS_LOCAL_JOB.getColumnHeader(), IS_LOCAL_JOB.getColumnHeader(),
MANIFEST_FILE_PATH.getColumnHeader(), MANIFEST_FILE_PATH.getColumnHeader(),
PRIORITY.getColumnHeader()}; PRIORITY.getColumnHeader(),
OCR.getColumnHeader()};
} }
/** /**
@ -406,6 +411,12 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
column.setMaxWidth(PRIORITY_COLUMN_MAX_WIDTH); column.setMaxWidth(PRIORITY_COLUMN_MAX_WIDTH);
column.setPreferredWidth(PRIORITY_COLUMN_PREFERRED_WIDTH); column.setPreferredWidth(PRIORITY_COLUMN_PREFERRED_WIDTH);
column.setWidth(PRIORITY_COLUMN_PREFERRED_WIDTH); column.setWidth(PRIORITY_COLUMN_PREFERRED_WIDTH);
column = pendingTable.getColumn(JobsTableModelColumns.OCR.getColumnHeader());
column.setCellRenderer(new OcrIconCellRenderer());
column.setMaxWidth(OCR_COLUMN_MAX_WIDTH);
column.setPreferredWidth(OCR_COLUMN_PREFERRED_WIDTH);
column.setWidth(OCR_COLUMN_PREFERRED_WIDTH);
/** /**
* Allow sorting when a column header is clicked. * Allow sorting when a column header is clicked.
@ -457,6 +468,8 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.IS_LOCAL_JOB.getColumnHeader())); runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.IS_LOCAL_JOB.getColumnHeader()));
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader())); runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader()));
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.PRIORITY.getColumnHeader())); runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.PRIORITY.getColumnHeader()));
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.OCR.getColumnHeader()));
/* /*
* Set up a column to display the cases associated with the jobs. * Set up a column to display the cases associated with the jobs.
*/ */
@ -553,6 +566,7 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader())); completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader()));
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader())); completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader()));
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.PRIORITY.getColumnHeader())); completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.PRIORITY.getColumnHeader()));
/* /*
* Set up a column to display the cases associated with the jobs. * Set up a column to display the cases associated with the jobs.
*/ */
@ -603,6 +617,15 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
column.setMaxWidth(STATUS_COL_MAX_WIDTH); column.setMaxWidth(STATUS_COL_MAX_WIDTH);
column.setPreferredWidth(STATUS_COL_PREFERRED_WIDTH); column.setPreferredWidth(STATUS_COL_PREFERRED_WIDTH);
column.setWidth(STATUS_COL_PREFERRED_WIDTH); column.setWidth(STATUS_COL_PREFERRED_WIDTH);
/*
* Set up a column to display OCR enabled/disabled flag.
*/
column = completedTable.getColumn(JobsTableModelColumns.OCR.getColumnHeader());
column.setCellRenderer(new OcrIconCellRenderer());
column.setMaxWidth(OCR_COLUMN_MAX_WIDTH);
column.setPreferredWidth(OCR_COLUMN_PREFERRED_WIDTH);
column.setWidth(OCR_COLUMN_PREFERRED_WIDTH);
/* /*
* Allow sorting when a column header is clicked. * Allow sorting when a column header is clicked.
@ -856,6 +879,7 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
case JOB_COMPLETED: case JOB_COMPLETED:
case CASE_DELETED: case CASE_DELETED:
case REPROCESS_JOB: case REPROCESS_JOB:
case OCR_STATE_CHANGE:
updateExecutor.submit(new UpdateAllJobsTablesTask()); updateExecutor.submit(new UpdateAllJobsTablesTask());
break; break;
case PAUSED_BY_USER_REQUEST: case PAUSED_BY_USER_REQUEST:
@ -1193,7 +1217,8 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
job.getCaseDirectoryPath(), // CASE_DIRECTORY_PATH job.getCaseDirectoryPath(), // CASE_DIRECTORY_PATH
job.getProcessingHostName().equals(LOCAL_HOST_NAME), // IS_LOCAL_JOB job.getProcessingHostName().equals(LOCAL_HOST_NAME), // IS_LOCAL_JOB
job.getManifest().getFilePath(), // MANIFEST_FILE_PATH job.getManifest().getFilePath(), // MANIFEST_FILE_PATH
job.getPriority()}); // PRIORITY job.getPriority(), // PRIORITY
job.getOcrEnabled()}); // OCR FLAG
} }
} catch (Exception ex) { } catch (Exception ex) {
sysLogger.log(Level.SEVERE, "Dashboard error refreshing table", ex); sysLogger.log(Level.SEVERE, "Dashboard error refreshing table", ex);

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -46,7 +46,7 @@ import org.sleuthkit.autopsy.ingest.IngestProgressSnapshotProvider;
final class AutoIngestJob implements Comparable<AutoIngestJob>, IngestProgressSnapshotProvider, Serializable { final class AutoIngestJob implements Comparable<AutoIngestJob>, IngestProgressSnapshotProvider, Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final int CURRENT_VERSION = 3; private static final int CURRENT_VERSION = 4;
private static final int DEFAULT_PRIORITY = 0; private static final int DEFAULT_PRIORITY = 0;
private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName(); private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName();
@ -100,6 +100,11 @@ final class AutoIngestJob implements Comparable<AutoIngestJob>, IngestProgressSn
private List<IngestThreadActivitySnapshot> ingestThreadsSnapshot; private List<IngestThreadActivitySnapshot> ingestThreadsSnapshot;
private List<Snapshot> ingestJobsSnapshot; private List<Snapshot> ingestJobsSnapshot;
private Map<String, Long> moduleRunTimesSnapshot; private Map<String, Long> moduleRunTimesSnapshot;
/*
* Version 4 fields.
*/
private boolean ocrEnabled;
/** /**
* Constructs a new automated ingest job. All job state not specified in the * Constructs a new automated ingest job. All job state not specified in the
@ -194,6 +199,11 @@ final class AutoIngestJob implements Comparable<AutoIngestJob>, IngestProgressSn
this.ingestJobsSnapshot = Collections.emptyList(); this.ingestJobsSnapshot = Collections.emptyList();
this.moduleRunTimesSnapshot = Collections.emptyMap(); this.moduleRunTimesSnapshot = Collections.emptyMap();
/*
* Version 4 fields
*/
this.ocrEnabled = nodeData.getOcrEnabled();
} catch (Exception ex) { } catch (Exception ex) {
throw new AutoIngestJobException(String.format("Error creating automated ingest job"), ex); throw new AutoIngestJobException(String.format("Error creating automated ingest job"), ex);
} }
@ -253,6 +263,24 @@ final class AutoIngestJob implements Comparable<AutoIngestJob>, IngestProgressSn
synchronized Integer getPriority() { synchronized Integer getPriority() {
return this.priority; return this.priority;
} }
/**
* Gets the OCR flag for the job.
*
* @return Flag whether OCR is enabled/disabled.
*/
synchronized boolean getOcrEnabled() {
return this.ocrEnabled;
}
/**
* Sets the OCR enabled/disabled flag for the job.
*
* @param enabled Flag whether OCR is enabled/disabled.
*/
synchronized void setOcrEnabled(boolean enabled) {
this.ocrEnabled = enabled;
}
/** /**
* Sets the processing stage of the job. The start date/time for the stage * Sets the processing stage of the job. The start date/time for the stage

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2017 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -31,7 +31,7 @@ import javax.lang.model.type.TypeKind;
*/ */
final class AutoIngestJobNodeData { final class AutoIngestJobNodeData {
private static final int CURRENT_VERSION = 2; private static final int CURRENT_VERSION = 3;
private static final int DEFAULT_PRIORITY = 0; private static final int DEFAULT_PRIORITY = 0;
/* /*
@ -47,7 +47,7 @@ final class AutoIngestJobNodeData {
* data. This avoids the need to continuously enlarge the buffer. Once the * data. This avoids the need to continuously enlarge the buffer. Once the
* buffer has all the necessary data, it will be resized as appropriate. * buffer has all the necessary data, it will be resized as appropriate.
*/ */
private static final int MAX_POSSIBLE_NODE_DATA_SIZE = 131637; private static final int MAX_POSSIBLE_NODE_DATA_SIZE = 131641;
/* /*
* Version 0 fields. * Version 0 fields.
@ -78,6 +78,11 @@ final class AutoIngestJobNodeData {
* Version 2 fields. * Version 2 fields.
*/ */
private long dataSourceSize; private long dataSourceSize;
/*
* Version 3 fields.
*/
private boolean ocrEnabled;
/** /**
* Gets the current version of the auto ingest job coordination service node * Gets the current version of the auto ingest job coordination service node
@ -115,6 +120,7 @@ final class AutoIngestJobNodeData {
setProcessingStageStartDate(job.getProcessingStageStartDate()); setProcessingStageStartDate(job.getProcessingStageStartDate());
setProcessingStageDetails(job.getProcessingStageDetails()); setProcessingStageDetails(job.getProcessingStageDetails());
setDataSourceSize(job.getDataSourceSize()); setDataSourceSize(job.getDataSourceSize());
setOcrEnabled(job.getOcrEnabled());
} }
/** /**
@ -128,7 +134,7 @@ final class AutoIngestJobNodeData {
if (null == nodeData || nodeData.length == 0) { if (null == nodeData || nodeData.length == 0) {
throw new InvalidDataException(null == nodeData ? "Null nodeData byte array" : "Zero-length nodeData byte array"); throw new InvalidDataException(null == nodeData ? "Null nodeData byte array" : "Zero-length nodeData byte array");
} }
/* /*
* Set default values for all fields. * Set default values for all fields.
*/ */
@ -150,6 +156,7 @@ final class AutoIngestJobNodeData {
this.processingStageDetailsDescription = ""; this.processingStageDetailsDescription = "";
this.processingStageDetailsStartDate = 0L; this.processingStageDetailsStartDate = 0L;
this.dataSourceSize = 0L; this.dataSourceSize = 0L;
this.ocrEnabled = false;
/* /*
* Get fields from node data. * Get fields from node data.
@ -192,6 +199,14 @@ final class AutoIngestJobNodeData {
*/ */
this.dataSourceSize = buffer.getLong(); this.dataSourceSize = buffer.getLong();
} }
if (buffer.hasRemaining()) {
/*
* Get version 3 fields.
*/
int ocrFlag = buffer.getInt();
this.ocrEnabled = (1 == ocrFlag);
}
} catch (BufferUnderflowException ex) { } catch (BufferUnderflowException ex) {
throw new InvalidDataException("Node data is incomplete", ex); throw new InvalidDataException("Node data is incomplete", ex);
@ -234,6 +249,24 @@ final class AutoIngestJobNodeData {
void setPriority(int priority) { void setPriority(int priority) {
this.priority = priority; this.priority = priority;
} }
/**
* Gets the OCR flag for the job.
*
* @return Flag whether OCR is enabled/disabled.
*/
boolean getOcrEnabled() {
return this.ocrEnabled;
}
/**
* Sets the OCR enabled/disabled flag for the job.
*
* @param enabled Flag whether OCR is enabled/disabled.
*/
void setOcrEnabled(boolean enabled) {
this.ocrEnabled = enabled;
}
/** /**
* Gets the number of times the job has crashed during processing. * Gets the number of times the job has crashed during processing.
@ -567,6 +600,10 @@ final class AutoIngestJobNodeData {
if (this.version >= 2) { if (this.version >= 2) {
buffer.putLong(this.dataSourceSize); buffer.putLong(this.dataSourceSize);
} }
if (this.version >= 3) {
buffer.putInt(this.ocrEnabled ? 1 : 0);
}
} }
// Prepare the array // Prepare the array

View File

@ -57,7 +57,8 @@ final class AutoIngestJobsNode extends AbstractNode {
"AutoIngestJobsNode.jobCreated.text=Job Created", "AutoIngestJobsNode.jobCreated.text=Job Created",
"AutoIngestJobsNode.jobCompleted.text=Job Completed", "AutoIngestJobsNode.jobCompleted.text=Job Completed",
"AutoIngestJobsNode.priority.text=Prioritized", "AutoIngestJobsNode.priority.text=Prioritized",
"AutoIngestJobsNode.status.text=Status" "AutoIngestJobsNode.status.text=Status",
"AutoIngestJobsNode.ocr.text=OCR"
}) })
/** /**
@ -98,12 +99,14 @@ final class AutoIngestJobsNode extends AbstractNode {
private final Stage jobStage; private final Stage jobStage;
private final List<Snapshot> jobSnapshot; private final List<Snapshot> jobSnapshot;
private final Integer jobPriority; private final Integer jobPriority;
private final Boolean ocrFlag;
AutoIngestJobWrapper(AutoIngestJob job) { AutoIngestJobWrapper(AutoIngestJob job) {
autoIngestJob = job; autoIngestJob = job;
jobStage = job.getProcessingStage(); jobStage = job.getProcessingStage();
jobSnapshot = job.getIngestJobSnapshots(); jobSnapshot = job.getIngestJobSnapshots();
jobPriority = job.getPriority(); jobPriority = job.getPriority();
ocrFlag = job.getOcrEnabled();
} }
AutoIngestJob getJob() { AutoIngestJob getJob() {
@ -123,11 +126,12 @@ final class AutoIngestJobsNode extends AbstractNode {
AutoIngestJob thisJob = this.autoIngestJob; AutoIngestJob thisJob = this.autoIngestJob;
AutoIngestJob otherJob = ((AutoIngestJobWrapper) other).autoIngestJob; AutoIngestJob otherJob = ((AutoIngestJobWrapper) other).autoIngestJob;
// Only equal if the manifest paths and processing stage details are the same. // Only equal if the manifest paths, processing stage details, priority, and OCR flag are the same.
return thisJob.getManifest().getFilePath().equals(otherJob.getManifest().getFilePath()) return thisJob.getManifest().getFilePath().equals(otherJob.getManifest().getFilePath())
&& jobStage.equals(((AutoIngestJobWrapper) other).jobStage) && jobStage.equals(((AutoIngestJobWrapper) other).jobStage)
&& jobSnapshot.equals(((AutoIngestJobWrapper) other).jobSnapshot) && jobSnapshot.equals(((AutoIngestJobWrapper) other).jobSnapshot)
&& jobPriority.equals(((AutoIngestJobWrapper) other).jobPriority); && jobPriority.equals(((AutoIngestJobWrapper) other).jobPriority)
&& ocrFlag.equals(((AutoIngestJobWrapper) other).ocrFlag);
} }
@Override @Override
@ -137,6 +141,7 @@ final class AutoIngestJobsNode extends AbstractNode {
hash = 23 * hash + Objects.hashCode(this.jobStage); hash = 23 * hash + Objects.hashCode(this.jobStage);
hash = 23 * hash + Objects.hashCode(this.jobSnapshot); hash = 23 * hash + Objects.hashCode(this.jobSnapshot);
hash = 23 * hash + Objects.hashCode(this.jobPriority); hash = 23 * hash + Objects.hashCode(this.jobPriority);
hash = 23 * hash + Objects.hashCode(this.ocrFlag);
return hash; return hash;
} }
@ -171,6 +176,10 @@ final class AutoIngestJobsNode extends AbstractNode {
Integer getPriority() { Integer getPriority() {
return autoIngestJob.getPriority(); return autoIngestJob.getPriority();
} }
boolean getOcrEnabled() {
return autoIngestJob.getOcrEnabled();
}
} }
/** /**
@ -327,6 +336,8 @@ final class AutoIngestJobsNode extends AbstractNode {
jobWrapper.getManifest().getDateFileCreated())); jobWrapper.getManifest().getDateFileCreated()));
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_priority_text(), Bundle.AutoIngestJobsNode_priority_text(), Bundle.AutoIngestJobsNode_priority_text(), ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_priority_text(), Bundle.AutoIngestJobsNode_priority_text(), Bundle.AutoIngestJobsNode_priority_text(),
jobWrapper.getPriority())); jobWrapper.getPriority()));
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_ocr_text(), Bundle.AutoIngestJobsNode_ocr_text(), Bundle.AutoIngestJobsNode_ocr_text(),
jobWrapper.getOcrEnabled()));
break; break;
case RUNNING_JOB: case RUNNING_JOB:
AutoIngestJob.StageDetails status = jobWrapper.getProcessingStageDetails(); AutoIngestJob.StageDetails status = jobWrapper.getProcessingStageDetails();
@ -344,6 +355,8 @@ final class AutoIngestJobsNode extends AbstractNode {
jobWrapper.getCompletedDate())); jobWrapper.getCompletedDate()));
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_status_text(), Bundle.AutoIngestJobsNode_status_text(), Bundle.AutoIngestJobsNode_status_text(), ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_status_text(), Bundle.AutoIngestJobsNode_status_text(), Bundle.AutoIngestJobsNode_status_text(),
jobWrapper.getErrorsOccurred() ? StatusIconCellRenderer.Status.WARNING : StatusIconCellRenderer.Status.OK)); jobWrapper.getErrorsOccurred() ? StatusIconCellRenderer.Status.WARNING : StatusIconCellRenderer.Status.OK));
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_ocr_text(), Bundle.AutoIngestJobsNode_ocr_text(), Bundle.AutoIngestJobsNode_ocr_text(),
jobWrapper.getOcrEnabled()));
break; break;
default: default:
} }
@ -364,6 +377,11 @@ final class AutoIngestJobsNode extends AbstractNode {
PrioritizationAction.DeprioritizeCaseAction deprioritizeCaseAction = new PrioritizationAction.DeprioritizeCaseAction(jobWrapper.getJob()); PrioritizationAction.DeprioritizeCaseAction deprioritizeCaseAction = new PrioritizationAction.DeprioritizeCaseAction(jobWrapper.getJob());
deprioritizeCaseAction.setEnabled(jobWrapper.getPriority() > 0); deprioritizeCaseAction.setEnabled(jobWrapper.getPriority() > 0);
actions.add(deprioritizeCaseAction); actions.add(deprioritizeCaseAction);
actions.add(new AutoIngestAdminActions.EnableOCR(jobWrapper.getJob()));
AutoIngestAdminActions.DisableOCR disableOCRAction = new AutoIngestAdminActions.DisableOCR(jobWrapper.getJob());
disableOCRAction.setEnabled(jobWrapper.getOcrEnabled() == true);
actions.add(disableOCRAction);
break; break;
case RUNNING_JOB: case RUNNING_JOB:
actions.add(new AutoIngestAdminActions.ProgressDialogAction(jobWrapper.getJob())); actions.add(new AutoIngestAdminActions.ProgressDialogAction(jobWrapper.getJob()));

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -43,6 +43,7 @@ final class AutoIngestJobsPanel extends javax.swing.JPanel implements ExplorerMa
private static final int INITIAL_CASENAME_WIDTH = 170; private static final int INITIAL_CASENAME_WIDTH = 170;
private static final int INITIAL_DATASOURCE_WIDTH = 270; private static final int INITIAL_DATASOURCE_WIDTH = 270;
private static final int INITIAL_PRIORITIZED_WIDTH = 20; private static final int INITIAL_PRIORITIZED_WIDTH = 20;
private static final int INITIAL_OCR_WIDTH = 20;
private static final int INITIAL_STATUS_WIDTH = 20; private static final int INITIAL_STATUS_WIDTH = 20;
private static final int INVALID_INDEX = -1; private static final int INVALID_INDEX = -1;
private final org.openide.explorer.view.OutlineView outlineView; private final org.openide.explorer.view.OutlineView outlineView;
@ -81,12 +82,18 @@ final class AutoIngestJobsPanel extends javax.swing.JPanel implements ExplorerMa
case PENDING_JOB: case PENDING_JOB:
outlineView.setPropertyColumns(Bundle.AutoIngestJobsNode_dataSource_text(), Bundle.AutoIngestJobsNode_dataSource_text(), outlineView.setPropertyColumns(Bundle.AutoIngestJobsNode_dataSource_text(), Bundle.AutoIngestJobsNode_dataSource_text(),
Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(),
Bundle.AutoIngestJobsNode_priority_text(), Bundle.AutoIngestJobsNode_priority_text()); Bundle.AutoIngestJobsNode_priority_text(), Bundle.AutoIngestJobsNode_priority_text(),
Bundle.AutoIngestJobsNode_ocr_text(), Bundle.AutoIngestJobsNode_ocr_text());
indexOfColumn = getColumnIndexByName(Bundle.AutoIngestJobsNode_priority_text()); indexOfColumn = getColumnIndexByName(Bundle.AutoIngestJobsNode_priority_text());
if (indexOfColumn != INVALID_INDEX) { if (indexOfColumn != INVALID_INDEX) {
outline.getColumnModel().getColumn(indexOfColumn).setPreferredWidth(INITIAL_PRIORITIZED_WIDTH); outline.getColumnModel().getColumn(indexOfColumn).setPreferredWidth(INITIAL_PRIORITIZED_WIDTH);
outline.getColumnModel().getColumn(indexOfColumn).setCellRenderer(new PrioritizedIconCellRenderer()); outline.getColumnModel().getColumn(indexOfColumn).setCellRenderer(new PrioritizedIconCellRenderer());
} }
indexOfColumn = getColumnIndexByName(Bundle.AutoIngestJobsNode_ocr_text());
if (indexOfColumn != INVALID_INDEX) {
outline.getColumnModel().getColumn(indexOfColumn).setPreferredWidth(INITIAL_OCR_WIDTH);
outline.getColumnModel().getColumn(indexOfColumn).setCellRenderer(new OcrIconCellRenderer());
}
break; break;
case RUNNING_JOB: case RUNNING_JOB:
outlineView.setPropertyColumns(Bundle.AutoIngestJobsNode_dataSource_text(), Bundle.AutoIngestJobsNode_dataSource_text(), outlineView.setPropertyColumns(Bundle.AutoIngestJobsNode_dataSource_text(), Bundle.AutoIngestJobsNode_dataSource_text(),
@ -102,7 +109,8 @@ final class AutoIngestJobsPanel extends javax.swing.JPanel implements ExplorerMa
outlineView.setPropertyColumns(Bundle.AutoIngestJobsNode_dataSource_text(), Bundle.AutoIngestJobsNode_dataSource_text(), outlineView.setPropertyColumns(Bundle.AutoIngestJobsNode_dataSource_text(), Bundle.AutoIngestJobsNode_dataSource_text(),
Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(),
Bundle.AutoIngestJobsNode_jobCompleted_text(), Bundle.AutoIngestJobsNode_jobCompleted_text(), Bundle.AutoIngestJobsNode_jobCompleted_text(), Bundle.AutoIngestJobsNode_jobCompleted_text(),
Bundle.AutoIngestJobsNode_status_text(), Bundle.AutoIngestJobsNode_status_text()); Bundle.AutoIngestJobsNode_status_text(), Bundle.AutoIngestJobsNode_status_text(),
Bundle.AutoIngestJobsNode_ocr_text(), Bundle.AutoIngestJobsNode_ocr_text());
indexOfColumn = getColumnIndexByName(Bundle.AutoIngestJobsNode_jobCompleted_text()); indexOfColumn = getColumnIndexByName(Bundle.AutoIngestJobsNode_jobCompleted_text());
if (indexOfColumn != INVALID_INDEX) { if (indexOfColumn != INVALID_INDEX) {
outline.setColumnSorted(indexOfColumn, false, 1); outline.setColumnSorted(indexOfColumn, false, 1);
@ -112,6 +120,11 @@ final class AutoIngestJobsPanel extends javax.swing.JPanel implements ExplorerMa
outline.getColumnModel().getColumn(indexOfColumn).setPreferredWidth(INITIAL_STATUS_WIDTH); outline.getColumnModel().getColumn(indexOfColumn).setPreferredWidth(INITIAL_STATUS_WIDTH);
outline.getColumnModel().getColumn(indexOfColumn).setCellRenderer(new StatusIconCellRenderer()); outline.getColumnModel().getColumn(indexOfColumn).setCellRenderer(new StatusIconCellRenderer());
} }
indexOfColumn = getColumnIndexByName(Bundle.AutoIngestJobsNode_ocr_text());
if (indexOfColumn != INVALID_INDEX) {
outline.getColumnModel().getColumn(indexOfColumn).setPreferredWidth(INITIAL_OCR_WIDTH);
outline.getColumnModel().getColumn(indexOfColumn).setCellRenderer(new OcrIconCellRenderer());
}
break; break;
default: default:
} }

View File

@ -105,6 +105,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleError;
import org.sleuthkit.autopsy.ingest.IngestStream; import org.sleuthkit.autopsy.ingest.IngestStream;
import org.sleuthkit.autopsy.keywordsearch.KeywordSearchModuleException; import org.sleuthkit.autopsy.keywordsearch.KeywordSearchModuleException;
import org.sleuthkit.autopsy.keywordsearch.Server; import org.sleuthkit.autopsy.keywordsearch.Server;
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
@ -144,7 +145,8 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
ControlEventType.SHUTDOWN.toString(), ControlEventType.SHUTDOWN.toString(),
ControlEventType.GENERATE_THREAD_DUMP_REQUEST.toString(), ControlEventType.GENERATE_THREAD_DUMP_REQUEST.toString(),
Event.CANCEL_JOB.toString(), Event.CANCEL_JOB.toString(),
Event.REPROCESS_JOB.toString()})); Event.REPROCESS_JOB.toString(),
Event.OCR_STATE_CHANGE.toString()}));
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED); private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
private static final long JOB_STATUS_EVENT_INTERVAL_SECONDS = 10; private static final long JOB_STATUS_EVENT_INTERVAL_SECONDS = 10;
private static final String JOB_STATUS_PUBLISHING_THREAD_NAME = "AIM-job-status-event-publisher-%d"; private static final String JOB_STATUS_PUBLISHING_THREAD_NAME = "AIM-job-status-event-publisher-%d";
@ -308,6 +310,8 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
handleRemoteJobCancelEvent((AutoIngestJobCancelEvent) event); handleRemoteJobCancelEvent((AutoIngestJobCancelEvent) event);
} else if (event instanceof AutoIngestJobReprocessEvent) { } else if (event instanceof AutoIngestJobReprocessEvent) {
handleRemoteJobReprocessEvent((AutoIngestJobReprocessEvent) event); handleRemoteJobReprocessEvent((AutoIngestJobReprocessEvent) event);
} else if (event instanceof AutoIngestOcrStateChangeEvent) {
handleRemoteOcrEvent((AutoIngestOcrStateChangeEvent) event);
} }
} }
} }
@ -466,10 +470,42 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
String hostName = event.getNodeName(); String hostName = event.getNodeName();
hostNamesToLastMsgTime.put(hostName, Instant.now()); hostNamesToLastMsgTime.put(hostName, Instant.now());
// currently the only way to the get latest ZK manifest node contents is to do an input directory scan
scanInputDirsNow(); scanInputDirsNow();
setChanged(); setChanged();
notifyObservers(Event.CASE_PRIORITIZED); notifyObservers(Event.CASE_PRIORITIZED);
} }
/**
* Processes a case OCR enabled/disabled event from another node.
*
* @param event OCR enabled/disabled event from another auto ingest node.
*/
private void handleRemoteOcrEvent(AutoIngestOcrStateChangeEvent event) {
switch (event.getEventType()) {
case OCR_ENABLED:
sysLogger.log(Level.INFO, "Received OCR enabled event for case {0} from user {1} on machine {2}",
new Object[]{event.getCaseName(), event.getUserName(), event.getNodeName()});
break;
case OCR_DISABLED:
sysLogger.log(Level.INFO, "Received OCR disabled event for case {0} from user {1} on machine {2}",
new Object[]{event.getCaseName(), event.getUserName(), event.getNodeName()});
break;
default:
sysLogger.log(Level.WARNING, "Received invalid OCR enabled/disabled event from user {0} on machine {1}",
new Object[]{event.getUserName(), event.getNodeName()});
break;
}
String hostName = event.getNodeName();
hostNamesToLastMsgTime.put(hostName, Instant.now());
// currently the only way to the get latest ZK manifest node contents is to do an input directory scan
scanInputDirsNow();
setChanged();
notifyObservers(Event.OCR_STATE_CHANGE);
}
/** /**
* Processes a case deletion event from another node by triggering an * Processes a case deletion event from another node by triggering an
@ -2057,10 +2093,11 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
} }
iterator.remove(); iterator.remove();
currentJob = job; // create a new job object based on latest ZK node data (i.e. instead of re-using potentially stale local pending AutoIngestJob object).
currentJob = new AutoIngestJob(nodeData);
break; break;
} catch (AutoIngestJobNodeData.InvalidDataException ex) { } catch (AutoIngestJobNodeData.InvalidDataException | AutoIngestJobException ex) {
sysLogger.log(Level.WARNING, String.format("Unable to use node data for %s", manifestPath), ex); sysLogger.log(Level.WARNING, String.format("Unable to use node data for %s", manifestPath), ex);
} }
} }
@ -2215,21 +2252,20 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* auto ingest job. * auto ingest job.
*/ */
private void attemptJob() throws CoordinationServiceException, SharedConfigurationException, ServicesMonitorException, DatabaseServerDownException, KeywordSearchServerDownException, CaseManagementException, AnalysisStartupException, FileExportException, AutoIngestJobLoggerException, InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, IOException, JobMetricsCollectionException { private void attemptJob() throws CoordinationServiceException, SharedConfigurationException, ServicesMonitorException, DatabaseServerDownException, KeywordSearchServerDownException, CaseManagementException, AnalysisStartupException, FileExportException, AutoIngestJobLoggerException, InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, IOException, JobMetricsCollectionException {
updateConfiguration();
if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) {
return;
}
verifyRequiredSevicesAreRunning(); verifyRequiredSevicesAreRunning();
if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) { if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) {
return; return;
} }
Case caseForJob = openCase(); Case caseForJob = openCase();
try { try {
if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) {
return;
}
updateConfiguration();
if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) { if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) {
return; return;
} }
runIngestForJob(caseForJob); runIngestForJob(caseForJob);
} finally { } finally {
try { try {
Case.closeCurrentCase(); Case.closeCurrentCase();
@ -2242,7 +2278,8 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
/** /**
* Updates the ingest system settings by downloading the latest version * Updates the ingest system settings by downloading the latest version
* of the settings if using shared configuration. * of the settings if using shared configuration. Also updates the OCR
* setting.
* *
* @throws SharedConfigurationException if there is an error downloading * @throws SharedConfigurationException if there is an error downloading
* shared configuration. * shared configuration.
@ -2258,6 +2295,15 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
currentJob.setProcessingStage(AutoIngestJob.Stage.UPDATING_SHARED_CONFIG, Date.from(Instant.now())); currentJob.setProcessingStage(AutoIngestJob.Stage.UPDATING_SHARED_CONFIG, Date.from(Instant.now()));
new SharedConfiguration().downloadConfiguration(); new SharedConfiguration().downloadConfiguration();
} }
// update the OCR enabled/disabled setting
if (currentJob.getOcrEnabled()) {
sysLogger.log(Level.INFO, "Enabling OCR for job {0}", currentJob.getManifest().getFilePath());
} else {
sysLogger.log(Level.INFO, "Disabling OCR for job {0}", currentJob.getManifest().getFilePath());
}
KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class);
kwsService.changeOcrState(currentJob.getOcrEnabled());
} }
/** /**
@ -3151,7 +3197,8 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
REPORT_STATE, REPORT_STATE,
CANCEL_JOB, CANCEL_JOB,
REPROCESS_JOB, REPROCESS_JOB,
GENERATE_THREAD_DUMP_RESPONSE GENERATE_THREAD_DUMP_RESPONSE,
OCR_STATE_CHANGE
} }
/** /**

View File

@ -84,7 +84,8 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
AutoIngestManager.Event.SHUTTING_DOWN.toString(), AutoIngestManager.Event.SHUTTING_DOWN.toString(),
AutoIngestManager.Event.SHUTDOWN.toString(), AutoIngestManager.Event.SHUTDOWN.toString(),
AutoIngestManager.Event.RESUMED.toString(), AutoIngestManager.Event.RESUMED.toString(),
AutoIngestManager.Event.GENERATE_THREAD_DUMP_RESPONSE.toString()})); AutoIngestManager.Event.GENERATE_THREAD_DUMP_RESPONSE.toString(),
AutoIngestManager.Event.OCR_STATE_CHANGE.toString()}));
private final AutopsyEventPublisher eventPublisher; private final AutopsyEventPublisher eventPublisher;
private CoordinationService coordinationService; private CoordinationService coordinationService;
private final ScheduledThreadPoolExecutor coordSvcQueryExecutor; private final ScheduledThreadPoolExecutor coordSvcQueryExecutor;
@ -166,6 +167,8 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
handleAutoIngestNodeStateEvent((AutoIngestNodeStateEvent) event); handleAutoIngestNodeStateEvent((AutoIngestNodeStateEvent) event);
} else if (event instanceof ThreadDumpResponseEvent) { } else if (event instanceof ThreadDumpResponseEvent) {
handleRemoteThreadDumpResponseEvent((ThreadDumpResponseEvent) event); handleRemoteThreadDumpResponseEvent((ThreadDumpResponseEvent) event);
} else if (event instanceof AutoIngestOcrStateChangeEvent) {
handleOcrStateChangeEvent((AutoIngestOcrStateChangeEvent) event);
} }
} }
@ -228,6 +231,15 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
} }
} }
/**
* Handles an OCR state change event.
*
* @param event OCR state change event.
*/
private void handleOcrStateChangeEvent(AutoIngestOcrStateChangeEvent event) {
coordSvcQueryExecutor.submit(new StateRefreshTask());
}
/** /**
* Handles an auto ingest job/case prioritization event. * Handles an auto ingest job/case prioritization event.
* *
@ -427,6 +439,51 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
return new JobsSnapshot(); return new JobsSnapshot();
} }
} }
/**
* Enables OCR for all pending ingest jobs for a specified case.
*
* @param caseName The name of the case to enable OCR.
*
* @throws AutoIngestMonitorException If there is an error enabling OCR for the jobs for the case.
*
*/
void changeOcrStateForCase(final String caseName, final boolean ocrState) throws AutoIngestMonitorException {
List<AutoIngestJob> jobsToPrioritize = new ArrayList<>();
synchronized (jobsLock) {
for (AutoIngestJob pendingJob : getPendingJobs()) {
if (pendingJob.getManifest().getCaseName().equals(caseName)) {
jobsToPrioritize.add(pendingJob);
}
}
if (!jobsToPrioritize.isEmpty()) {
for (AutoIngestJob job : jobsToPrioritize) {
String manifestNodePath = job.getManifest().getFilePath().toString();
try {
AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(coordinationService.getNodeData(CoordinationService.CategoryNode.MANIFESTS, manifestNodePath));
nodeData.setOcrEnabled(ocrState);
coordinationService.setNodeData(CoordinationService.CategoryNode.MANIFESTS, manifestNodePath, nodeData.toArray());
} catch (AutoIngestJobNodeData.InvalidDataException | CoordinationServiceException | InterruptedException ex) {
throw new AutoIngestMonitorException("Error enabling OCR for job " + job.toString(), ex);
}
job.setOcrEnabled(ocrState);
/**
* Update job object in pending jobs queue
*/
jobsSnapshot.addOrReplacePendingJob(job);
}
/*
* Publish the OCR enabled event.
*/
new Thread(() -> {
eventPublisher.publishRemotely(new AutoIngestOcrStateChangeEvent(LOCAL_HOST_NAME, caseName,
AutoIngestManager.getSystemUserNameProperty(), ocrState));
}).start();
}
}
}
/** /**
* Removes the priority (set to zero) of all pending ingest jobs for a * Removes the priority (set to zero) of all pending ingest jobs for a

View File

@ -69,7 +69,7 @@ class AutoIngestNodeRefreshEvents {
private final String caseName; private final String caseName;
/** /**
* Contructs a RefreshCaseEvent * Constructs a RefreshCaseEvent
* *
* @param monitor The monitor that will provide access to the current state of the jobs lists. * @param monitor The monitor that will provide access to the current state of the jobs lists.
* @param name The name of the case whose nodes should be refreshed. * @param name The name of the case whose nodes should be refreshed.

View File

@ -0,0 +1,99 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 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.experimental.autoingest;
import java.io.Serializable;
import org.sleuthkit.autopsy.events.AutopsyEvent;
/**
* Event published when an automated ingest manager enables or disables OCR on a case.
*/
public final class AutoIngestOcrStateChangeEvent extends AutopsyEvent implements Serializable {
/**
* Possible event types
*/
enum EventType {
OCR_ENABLED,
OCR_DISABLED
}
private static final long serialVersionUID = 1L;
private final String caseName;
private final String nodeName;
private final String userName;
private final EventType eventType;
/**
* Constructs an event published when an automated ingest manager
* enables or disables OCR on a case.
*
* @param caseName The name of the case.
* @param nodeName The host name of the node that enabled/disabled OCR.
* @param userName The logged in user
* @param ocrState Flag whether OCR is enabled/disabled
*/
public AutoIngestOcrStateChangeEvent(String nodeName, String caseName, String userName, boolean ocrState) {
super(AutoIngestManager.Event.OCR_STATE_CHANGE.toString(), null, null);
this.caseName = caseName;
this.nodeName = nodeName;
this.userName = userName;
if (ocrState == true) {
this.eventType = EventType.OCR_ENABLED;
} else {
this.eventType = EventType.OCR_DISABLED;
}
}
/**
* Gets the name of the prioritized case.
*
* @return The case name.
*/
public String getCaseName() {
return caseName;
}
/**
* Gets the host name of the node that prioritized the case.
*
* @return The host name of the node.
*/
public String getNodeName() {
return nodeName;
}
/**
* Gets the user logged in to the node that prioritized the case.
*
* @return The user name
*/
String getUserName() {
return userName;
}
/**
* Gets the type of prioritization
*
* @return The type
*/
EventType getEventType() {
return eventType;
}
}

View File

@ -10,6 +10,10 @@ AinStatusNode.status.title=Status
AinStatusNode.status.unknown=Unknown AinStatusNode.status.unknown=Unknown
AutoIngestAdminActions.cancelJobAction.title=Cancel Job AutoIngestAdminActions.cancelJobAction.title=Cancel Job
AutoIngestAdminActions.cancelModuleAction.title=Cancel Module AutoIngestAdminActions.cancelModuleAction.title=Cancel Module
AutoIngestAdminActions.disableOCR.error=Failed to disable OCR for case "%s".
AutoIngestAdminActions.disableOCR.title=Disable OCR For This Case
AutoIngestAdminActions.enableOCR.error=Failed to enable OCR for case "%s".
AutoIngestAdminActions.enableOCR.title=Enable OCR For This Case
AutoIngestAdminActions.getThreadDump.title=Generate Thread Dump AutoIngestAdminActions.getThreadDump.title=Generate Thread Dump
AutoIngestAdminActions.pause.title=Pause Node AutoIngestAdminActions.pause.title=Pause Node
AutoIngestAdminActions.progressDialogAction.title=Ingest Progress AutoIngestAdminActions.progressDialogAction.title=Ingest Progress
@ -71,6 +75,7 @@ AutoIngestControlPanel.JobsTableModel.ColumnHeader.HostName=Host Name
AutoIngestControlPanel.JobsTableModel.ColumnHeader.ImageFolder=Data Source AutoIngestControlPanel.JobsTableModel.ColumnHeader.ImageFolder=Data Source
AutoIngestControlPanel.JobsTableModel.ColumnHeader.LocalJob=\ Local Job? AutoIngestControlPanel.JobsTableModel.ColumnHeader.LocalJob=\ Local Job?
AutoIngestControlPanel.JobsTableModel.ColumnHeader.ManifestFilePath=\ Manifest File Path AutoIngestControlPanel.JobsTableModel.ColumnHeader.ManifestFilePath=\ Manifest File Path
AutoIngestControlPanel.JobsTableModel.ColumnHeader.OCR=OCR
AutoIngestControlPanel.JobsTableModel.ColumnHeader.Priority=Prioritized AutoIngestControlPanel.JobsTableModel.ColumnHeader.Priority=Prioritized
AutoIngestControlPanel.JobsTableModel.ColumnHeader.Stage=Stage AutoIngestControlPanel.JobsTableModel.ColumnHeader.Stage=Stage
AutoIngestControlPanel.JobsTableModel.ColumnHeader.StageTime=Time in Stage AutoIngestControlPanel.JobsTableModel.ColumnHeader.StageTime=Time in Stage
@ -130,6 +135,7 @@ AutoIngestJobsNode.dataSource.text=Data Source
AutoIngestJobsNode.hostName.text=Host Name AutoIngestJobsNode.hostName.text=Host Name
AutoIngestJobsNode.jobCompleted.text=Job Completed AutoIngestJobsNode.jobCompleted.text=Job Completed
AutoIngestJobsNode.jobCreated.text=Job Created AutoIngestJobsNode.jobCreated.text=Job Created
AutoIngestJobsNode.ocr.text=OCR
AutoIngestJobsNode.prioritized.false=No AutoIngestJobsNode.prioritized.false=No
AutoIngestJobsNode.prioritized.true=Yes AutoIngestJobsNode.prioritized.true=Yes
AutoIngestJobsNode.priority.text=Prioritized AutoIngestJobsNode.priority.text=Prioritized
@ -206,9 +212,7 @@ DeleteCaseTask.progress.parsingManifest=Parsing manifest file {0}...
DeleteCaseTask.progress.releasingManifestLock=Releasing lock on the manifest file {0}... DeleteCaseTask.progress.releasingManifestLock=Releasing lock on the manifest file {0}...
DeleteCaseTask.progress.startMessage=Starting deletion... DeleteCaseTask.progress.startMessage=Starting deletion...
DeleteOrphanCaseNodesAction.progressDisplayName=Cleanup Case Znodes DeleteOrphanCaseNodesAction.progressDisplayName=Cleanup Case Znodes
# {0} - item count
DeleteOrphanCaseNodesDialog.additionalInit.lblNodeCount.text=Znodes found: {0} DeleteOrphanCaseNodesDialog.additionalInit.lblNodeCount.text=Znodes found: {0}
# {0} - item count
DeleteOrphanCaseNodesDialog.additionalInit.znodesTextArea.countMessage=ZNODES FOUND: {0} DeleteOrphanCaseNodesDialog.additionalInit.znodesTextArea.countMessage=ZNODES FOUND: {0}
DeleteOrphanCaseNodesTask.progress.connectingToCoordSvc=Connecting to the coordination service DeleteOrphanCaseNodesTask.progress.connectingToCoordSvc=Connecting to the coordination service
# {0} - node path # {0} - node path
@ -224,6 +228,8 @@ DeleteOrphanManifestNodesTask.progress.gettingManifestNodes=Querying the coordin
DeleteOrphanManifestNodesTask.progress.lookingForOrphanedManifestFileZnodes=Looking for orphaned manifest file znodes DeleteOrphanManifestNodesTask.progress.lookingForOrphanedManifestFileZnodes=Looking for orphaned manifest file znodes
DeleteOrphanManifestNodesTask.progress.startMessage=Starting orphaned manifest file znode cleanup DeleteOrphanManifestNodesTask.progress.startMessage=Starting orphaned manifest file znode cleanup
HINT_CasesDashboardTopComponent=This is an adminstrative dashboard for multi-user cases HINT_CasesDashboardTopComponent=This is an adminstrative dashboard for multi-user cases
OcrIconCellRenderer.disabled.tooltiptext=This job does not have OCR enabled.
OcrIconCellRenderer.enabled.tooltiptext=This job has OCR enabled.
OpenAutoIngestLogAction.deletedLogErrorMsg=The case auto ingest log has been deleted. OpenAutoIngestLogAction.deletedLogErrorMsg=The case auto ingest log has been deleted.
OpenAutoIngestLogAction.logOpenFailedErrorMsg=Failed to open case auto ingest log. See application log for details. OpenAutoIngestLogAction.logOpenFailedErrorMsg=Failed to open case auto ingest log. See application log for details.
OpenAutoIngestLogAction.menuItemText=Open Auto Ingest Log File OpenAutoIngestLogAction.menuItemText=Open Auto Ingest Log File
@ -333,7 +339,7 @@ PrioritizationAction.deprioritizeCaseAction.error=Failed to deprioritize case "%
PrioritizationAction.deprioritizeCaseAction.title=Deprioritize Case PrioritizationAction.deprioritizeCaseAction.title=Deprioritize Case
PrioritizationAction.deprioritizeJobAction.error=Failed to deprioritize job "%s". PrioritizationAction.deprioritizeJobAction.error=Failed to deprioritize job "%s".
PrioritizationAction.deprioritizeJobAction.title=Deprioritize Job PrioritizationAction.deprioritizeJobAction.title=Deprioritize Job
PrioritizationAction.prioritizeCaseAction.error==Failed to prioritize case "%s". PrioritizationAction.prioritizeCaseAction.error=Failed to prioritize case "%s".
PrioritizationAction.prioritizeCaseAction.title=Prioritize Case PrioritizationAction.prioritizeCaseAction.title=Prioritize Case
PrioritizationAction.prioritizeJobAction.error=Failed to prioritize job "%s". PrioritizationAction.prioritizeJobAction.error=Failed to prioritize job "%s".
PrioritizationAction.prioritizeJobAction.title=Prioritize Job PrioritizationAction.prioritizeJobAction.title=Prioritize Job

View File

@ -0,0 +1,72 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 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.experimental.autoingest;
import java.awt.Component;
import java.lang.reflect.InvocationTargetException;
import javax.swing.ImageIcon;
import javax.swing.JTable;
import static javax.swing.SwingConstants.CENTER;
import org.openide.nodes.Node;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.guiutils.GrayableCellRenderer;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
/**
* A JTable and Outline view cell renderer that represents whether OCR is enabled for the job.
*/
class OcrIconCellRenderer extends GrayableCellRenderer {
@Messages({
"OcrIconCellRenderer.enabled.tooltiptext=This job has OCR enabled.",
"OcrIconCellRenderer.disabled.tooltiptext=This job does not have OCR enabled."
})
private static final long serialVersionUID = 1L;
static final ImageIcon checkedIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/tick.png", false));
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setHorizontalAlignment(CENTER);
Object switchValue = null;
if ((value instanceof NodeProperty)) {
//The Outline view has properties in the cell, the value contained in the property is what we want
try {
switchValue = ((Node.Property) value).getValue();
} catch (IllegalAccessException | InvocationTargetException ignored) {
//Unable to get the value from the NodeProperty no Icon will be displayed
}
} else {
//JTables contain the value we want directly in the cell
switchValue = value;
}
if (switchValue instanceof Boolean && (boolean) switchValue == true) {
setIcon(checkedIcon);
setToolTipText(org.openide.util.NbBundle.getMessage(OcrIconCellRenderer.class, "OcrIconCellRenderer.enabled.tooltiptext"));
} else {
setIcon(null);
if (switchValue instanceof Boolean) {
setToolTipText(org.openide.util.NbBundle.getMessage(OcrIconCellRenderer.class, "OcrIconCellRenderer.disabled.tooltiptext"));
}
}
grayCellIfTableNotEnabled(table, isSelected);
return this;
}
}

View File

@ -193,7 +193,7 @@ abstract class PrioritizationAction extends AbstractAction {
* AutoIngestJob is a part of. * AutoIngestJob is a part of.
*/ */
@Messages({"PrioritizationAction.prioritizeCaseAction.title=Prioritize Case", @Messages({"PrioritizationAction.prioritizeCaseAction.title=Prioritize Case",
"PrioritizationAction.prioritizeCaseAction.error==Failed to prioritize case \"%s\"."}) "PrioritizationAction.prioritizeCaseAction.error=Failed to prioritize case \"%s\"."})
static final class PrioritizeCaseAction extends PrioritizationAction { static final class PrioritizeCaseAction extends PrioritizationAction {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2020 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -29,10 +29,10 @@ import java.util.List;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JLabel;
import javax.swing.SizeRequirements; import javax.swing.SizeRequirements;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.text.Element; import javax.swing.text.Element;
import javax.swing.text.View; import javax.swing.text.View;
import javax.swing.text.ViewFactory; import javax.swing.text.ViewFactory;
@ -59,7 +59,7 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
private static final Logger logger = Logger.getLogger(ExtractedContentPanel.class.getName()); private static final Logger logger = Logger.getLogger(ExtractedContentPanel.class.getName());
// set font as close as possible to default // set font as close as possible to default
private static final Font DEFAULT_FONT = new JLabel().getFont(); private static final Font DEFAULT_FONT = UIManager.getDefaults().getFont("Label.font");
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private String contentName; private String contentName;
@ -72,7 +72,6 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
ExtractedContentPanel() { ExtractedContentPanel() {
initComponents(); initComponents();
additionalInit(); additionalInit();
setSources("", new ArrayList<>());
hitPreviousButton.setEnabled(false); hitPreviousButton.setEnabled(false);
hitNextButton.setEnabled(false); hitNextButton.setEnabled(false);
@ -135,7 +134,7 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
}; };
} }
}; };
// get the style sheet for editing font size // set new style sheet to clear default styles
styleSheet = editorKit.getStyleSheet(); styleSheet = editorKit.getStyleSheet();
sourceComboBox.addItemListener(itemEvent -> { sourceComboBox.addItemListener(itemEvent -> {
@ -144,6 +143,7 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
} }
}); });
extractedTextPane.setComponentPopupMenu(rightClickMenu); extractedTextPane.setComponentPopupMenu(rightClickMenu);
copyMenuItem.addActionListener(actionEvent -> extractedTextPane.copy()); copyMenuItem.addActionListener(actionEvent -> extractedTextPane.copy());
selectAllMenuItem.addActionListener(actionEvent -> extractedTextPane.selectAll()); selectAllMenuItem.addActionListener(actionEvent -> extractedTextPane.selectAll());
@ -156,11 +156,16 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
if (zoomPanel instanceof TextZoomPanel) if (zoomPanel instanceof TextZoomPanel)
((TextZoomPanel) this.zoomPanel).resetSize(); ((TextZoomPanel) this.zoomPanel).resetSize();
}); });
setSources("", new ArrayList<>());
} }
private void setStyleSheetSize(StyleSheet styleSheet, int size) { private void setStyleSheetSize(StyleSheet styleSheet, int size) {
styleSheet.addRule("body {font-family:\"" + DEFAULT_FONT.getFamily() + "\"; font-size:" + size + "pt; } "); styleSheet.addRule(
"body { font-family:\"" + DEFAULT_FONT.getFamily() + "\"; font-size:" + size + "pt; } " +
"pre { font-family:\"" + DEFAULT_FONT.getFamily() + "\"; font-size:" + size + "pt; } "
);
} }
@ -499,6 +504,8 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
extractedTextPane.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); extractedTextPane.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
} }
// refresh style
setStyleSheetSize(styleSheet, curSize);
extractedTextPane.setText(safeText); extractedTextPane.setText(safeText);
extractedTextPane.setCaretPosition(0); extractedTextPane.setCaretPosition(0);
} }

View File

@ -462,4 +462,17 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService {
} }
} }
/**
* A flag to enable or disable OCR on all future text indexing. Also sets the
* the "Limited OCR" functionality accordingly.
*
* @param state Boolean flag to enable/disable OCR. Set to True to enable
* OCR, or False to disable it.
*/
@Override
public void changeOcrState(boolean state) {
KeywordSearchSettings.setOcrOption(state);
KeywordSearchSettings.setLimitedOcrOption(state);
}
} }

View File

@ -596,8 +596,9 @@ final class ChromeCacheExtractor {
if (fileCopyCache.containsKey(fileTableKey)) { if (fileCopyCache.containsKey(fileTableKey)) {
return Optional.of(fileCopyCache.get(fileTableKey).getAbstractFile()); return Optional.of(fileCopyCache.get(fileTableKey).getAbstractFile());
} }
List<AbstractFile> cacheFiles = fileManager.findFiles(dataSource, cacheFileName, cacheFolderName); //NON-NLS List<AbstractFile> cacheFiles = currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource,
cacheFileName, cacheFolderName);
if (!cacheFiles.isEmpty()) { if (!cacheFiles.isEmpty()) {
// Sort the list for consistency. Preference is: // Sort the list for consistency. Preference is:
// - In correct subfolder and allocated // - In correct subfolder and allocated

View File

@ -633,7 +633,7 @@ class Chromium extends Extract {
BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes); BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes);
bbartifacts.add(webDownloadArtifact); bbartifacts.add(webDownloadArtifact);
String normalizedFullPath = FilenameUtils.normalize(fullPath, true); String normalizedFullPath = FilenameUtils.normalize(fullPath, true);
for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(normalizedFullPath), FilenameUtils.getPath(normalizedFullPath))) { for (AbstractFile downloadedFile : currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource, FilenameUtils.getName(normalizedFullPath), FilenameUtils.getPath(normalizedFullPath))) {
bbartifacts.add(createAssociatedArtifact(downloadedFile, webDownloadArtifact)); bbartifacts.add(createAssociatedArtifact(downloadedFile, webDownloadArtifact));
break; break;
} }

View File

@ -157,17 +157,13 @@ class DataSourceUsageAnalyzer extends Extract {
* does not exist with the given description. * does not exist with the given description.
* *
* @param osType - the OS_TYPE to check for * @param osType - the OS_TYPE to check for
*
* @return true if any specified files exist false if none exist
*/ */
private void checkIfOsSpecificVolume(ExtractOs.OS_TYPE osType) throws TskCoreException { private void checkIfOsSpecificVolume(ExtractOs.OS_TYPE osType) throws TskCoreException {
FileManager fileManager = currentCase.getServices().getFileManager();
for (String filePath : osType.getFilePaths()) { for (String filePath : osType.getFilePaths()) {
for (AbstractFile file : fileManager.findFiles(dataSource, FilenameUtils.getName(filePath), FilenameUtils.getPath(filePath))) { for (AbstractFile file : currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource,
if ((file.getParentPath() + file.getName()).equals(filePath)) { FilenameUtils.getName(filePath), FilenameUtils.getPath(filePath))) {
createDataSourceUsageArtifact(osType.getDsUsageLabel()); createDataSourceUsageArtifact(osType.getDsUsageLabel());
return; return;
}
} }
} }
} }

View File

@ -117,12 +117,10 @@ class ExtractOs extends Extract {
* search for * search for
*/ */
private AbstractFile getFirstFileFound(List<String> pathsToSearchFor) throws TskCoreException{ private AbstractFile getFirstFileFound(List<String> pathsToSearchFor) throws TskCoreException{
FileManager fileManager = currentCase.getServices().getFileManager();
for (String filePath : pathsToSearchFor) { for (String filePath : pathsToSearchFor) {
for (AbstractFile file : fileManager.findFiles(dataSource, FilenameUtils.getName(filePath), FilenameUtils.getPath(filePath))) { List<AbstractFile> files = currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource, FilenameUtils.getName(filePath), FilenameUtils.getPath(filePath));
if ((file.getParentPath() + file.getName()).equals(filePath)) { if (!files.isEmpty()) {
return file; return files.get(0);
}
} }
} }
return null; return null;

View File

@ -1686,18 +1686,13 @@ class ExtractRegistry extends Extract {
* @returnv BlackboardArtifact or a null value * @returnv BlackboardArtifact or a null value
*/ */
private BlackboardArtifact createAssociatedArtifact(String filePathName, BlackboardArtifact bba) { private BlackboardArtifact createAssociatedArtifact(String filePathName, BlackboardArtifact bba) {
org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager();
String fileName = FilenameUtils.getName(filePathName); String fileName = FilenameUtils.getName(filePathName);
String filePath = FilenameUtils.getPath(filePathName); String filePath = FilenameUtils.getPath(filePathName);
List<AbstractFile> sourceFiles; List<AbstractFile> sourceFiles;
try { try {
sourceFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS sourceFiles = currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource, fileName, filePath);
if (!sourceFiles.isEmpty()) { if (!sourceFiles.isEmpty()) {
for (AbstractFile sourceFile : sourceFiles) { return createAssociatedArtifact(sourceFiles.get(0), bba);
if (sourceFile.getParentPath().endsWith(filePath)) {
return createAssociatedArtifact(sourceFile, bba);
}
}
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
// only catching the error and displaying the message as the file may not exist on the // only catching the error and displaying the message as the file may not exist on the

View File

@ -647,8 +647,6 @@ final class ExtractSafari extends Extract {
Long time = null; Long time = null;
Long pathID = null; Long pathID = null;
FileManager fileManager = getCurrentCase().getServices().getFileManager();
NSString nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_URL); NSString nsstring = (NSString) entry.get(PLIST_KEY_DOWNLOAD_URL);
if (nsstring != null) { if (nsstring != null) {
url = nsstring.toString(); url = nsstring.toString();
@ -669,7 +667,8 @@ final class ExtractSafari extends Extract {
bbartifacts.add(webDownloadArtifact); bbartifacts.add(webDownloadArtifact);
// find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact. // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact.
for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(path), FilenameUtils.getPath(path))) { for (AbstractFile downloadedFile : currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource,
FilenameUtils.getName(path), FilenameUtils.getPath(path))) {
bbartifacts.add(createAssociatedArtifact(downloadedFile, webDownloadArtifact)); bbartifacts.add(createAssociatedArtifact(downloadedFile, webDownloadArtifact));
break; break;
} }

View File

@ -592,7 +592,8 @@ class Firefox extends Extract {
bbartifacts.add(webDownloadArtifact); bbartifacts.add(webDownloadArtifact);
// find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact. // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact.
for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) { for (AbstractFile downloadedFile : currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource,
FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
bbartifacts.add(createAssociatedArtifact(downloadedFile, webDownloadArtifact)); bbartifacts.add(createAssociatedArtifact(downloadedFile, webDownloadArtifact));
break; break;
} }
@ -727,7 +728,8 @@ class Firefox extends Extract {
bbartifacts.add(webDownloadArtifact); bbartifacts.add(webDownloadArtifact);
// find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact. // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact.
for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) { for (AbstractFile downloadedFile : currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource,
FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
bbartifacts.add(createAssociatedArtifact(downloadedFile, webDownloadArtifact)); bbartifacts.add(createAssociatedArtifact(downloadedFile, webDownloadArtifact));
break; break;
} }

View File

@ -152,13 +152,12 @@ class RecentDocumentsByLnk extends Extract {
* @returnv BlackboardArtifact or a null value * @returnv BlackboardArtifact or a null value
*/ */
private BlackboardArtifact createAssociatedArtifact(String filePathName, BlackboardArtifact bba) { private BlackboardArtifact createAssociatedArtifact(String filePathName, BlackboardArtifact bba) {
org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager();
String normalizePathName = FilenameUtils.normalize(filePathName, true); String normalizePathName = FilenameUtils.normalize(filePathName, true);
String fileName = FilenameUtils.getName(normalizePathName); String fileName = FilenameUtils.getName(normalizePathName);
String filePath = FilenameUtils.getPath(normalizePathName); String filePath = FilenameUtils.getPath(normalizePathName);
List<AbstractFile> sourceFiles; List<AbstractFile> sourceFiles;
try { try {
sourceFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS sourceFiles = currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource, fileName, filePath);
for (AbstractFile sourceFile : sourceFiles) { for (AbstractFile sourceFile : sourceFiles) {
if (sourceFile.getParentPath().endsWith(filePath)) { if (sourceFile.getParentPath().endsWith(filePath)) {
return createAssociatedArtifact(sourceFile, bba); return createAssociatedArtifact(sourceFile, bba);

View File

@ -135,8 +135,7 @@ class Util {
parent_path = parent_path.substring(0, index); parent_path = parent_path.substring(0, index);
List<AbstractFile> files = null; List<AbstractFile> files = null;
try { try {
FileManager fileManager = Case.getCurrentCaseThrows().getServices().getFileManager(); files = Case.getCurrentCaseThrows().getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource, name, parent_path);
files = fileManager.findFiles(dataSource, name, parent_path);
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Error fetching 'index.data' files for Internet Explorer history."); //NON-NLS logger.log(Level.WARNING, "Error fetching 'index.data' files for Internet Explorer history."); //NON-NLS
} }