From 73b39c6360d84f02275b9b716e461f7d85bf5f41 Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 8 Apr 2019 16:19:11 -0400 Subject: [PATCH 01/57] Added BaseChildFactory class that can provide paging and filtering support to subclasses. --- .../autopsy/core/UserPreferences.java | 19 ++ .../autopsy/datamodel/BaseChildFactory.java | 223 ++++++++++++++++++ .../datamodel/KnownAndSlackFilter.java | 60 +++++ 3 files changed, 302 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java create mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/KnownAndSlackFilter.java diff --git a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java index 9efcce9f7d..b58e310fe0 100644 --- a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java +++ b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java @@ -76,6 +76,7 @@ public final class UserPreferences { public static final String DISPLAY_TRANSLATED_NAMES = "DisplayTranslatedNames"; public static final String EXTERNAL_HEX_EDITOR_PATH = "ExternalHexEditorPath"; public static final String SOLR_MAX_JVM_SIZE = "SolrMaxJVMSize"; + public static final String RESULTS_TABLE_PAGE_SIZE = "ResultsTablePageSize"; // Prevent instantiation. private UserPreferences() { @@ -491,6 +492,24 @@ public final class UserPreferences { preferences.putInt(SOLR_MAX_JVM_SIZE, maxSize); } + /** + * Get the maximum number of results to display in a result table. + * + * @return Saved value or default (0) which indicates no max. + */ + public static int getResultsTablePageSize() { + return preferences.getInt(RESULTS_TABLE_PAGE_SIZE, 0); + } + + /** + * Set the maximum number of results to display in a result table. + * + * @param pageSize + */ + public static void setResultsTablePageSize(int pageSize) { + preferences.putInt(RESULTS_TABLE_PAGE_SIZE, pageSize); + } + /** * Set the HdX path. * diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java new file mode 100644 index 0000000000..c192d88a98 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java @@ -0,0 +1,223 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datamodel; + +import com.google.common.collect.Lists; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import org.openide.nodes.ChildFactory; +import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.datamodel.Content; + +/** + * Abstract child factory that provides paging and filtering functionality + * to subclasses. + * @param + */ +public abstract class BaseChildFactory extends ChildFactory.Detachable { + + private final Predicate filter; + private boolean isPageChangeEvent; + + private final PagingSupport pagingSupport; + + /** + * This static map is used to facilitate communication between the UI + * and the child factory. + */ + public static Map nodeNameToEventBusMap = new ConcurrentHashMap<>(); + + public BaseChildFactory(String nodeName) { + pagingSupport = new PagingSupport(nodeName); + isPageChangeEvent = false; + filter = new KnownAndSlackFilter<>(); + } + + @Override + protected void addNotify() { + onAdd(); + pagingSupport.initialize(); + } + + @Override + protected void removeNotify() { + onRemove(); + pagingSupport.destroy(); + } + + /** + * Subclasses implement this to construct a collection of keys. + * @return + */ + protected abstract List makeKeys(); + + /** + * Subclasses implement this to initialize any required resources. + */ + protected abstract void onAdd(); + + /** + * Subclasses implement this to clean up any resources they + * acquired in onAdd() + */ + protected abstract void onRemove(); + + @Override + protected boolean createKeys(List toPopulate) { + // For page chage events we simply return the previously calculated + // keys, otherwise we make a new set of keys. + if (!isPageChangeEvent) { + List allKeys = makeKeys(); + + // Filter keys + allKeys.stream().filter(filter).collect(Collectors.toList()); + + pagingSupport.splitKeysIntoPages(allKeys); + } + + toPopulate.addAll(pagingSupport.getCurrentPage()); + + // Reset page change event flag + isPageChangeEvent = false; + + return true; + } + + /** + * Class that supplies paging related functionality to the base + * child factory class. + */ + class PagingSupport { + + private final String nodeName; + private final int pageSize; + private int currentPage; + private List> pages; + private EventBus bus; + + /** + * Construct PagingSupport instance for the given node name. + * @param nodeName Name of the node in the tree for which results + * are being displayed. The node name is used to allow communication + * between the UI and the ChildFactory via an EventBus. + */ + PagingSupport(String nodeName) { + pageSize = UserPreferences.getResultsTablePageSize(); + this.currentPage = 1; + pages = new ArrayList<>(); + this.nodeName = nodeName; + } + + void initialize() { + if (pageSize > 0) { + // Only configure an EventBus if paging functionality is enabled. + bus = new EventBus(nodeName); + nodeNameToEventBusMap.put(bus.identifier(), bus); + bus.register(this); + } + } + + void destroy() { + if (bus != null) { + nodeNameToEventBusMap.remove(bus.identifier()); + bus.unregister(this); + bus.post(new PagingDestroyedEvent()); + bus = null; + } + } + + /** + * Get the list of keys at the current page. + * @return List of keys. + */ + List getCurrentPage() { + if (pages.size() > 0) { + return pages.get(currentPage - 1); + } + + return Collections.emptyList(); + } + + /** + * Split the given collection of keys into pages based on page size. + * @param keys + */ + void splitKeysIntoPages(List keys) { + int oldPageCount = pages.size(); + + /** + * If pageSize is set split keys into pages, + * otherwise create a single page containing all keys. + */ + pages = Lists.partition(keys, pageSize > 0 ? pageSize : keys.size()); + if (pages.size() != oldPageCount) { + // Number of pages has changed so we need to send out a notification. + bus.post(new PageCountChangeEvent(pages.size())); + } + } + + @Subscribe + private void subscribeToPageChange(PageChangeEvent event) { + // Receives page change events from UI components and + // triggers a refresh in the child factory. + if (event != null) { + currentPage = event.getPageNumber(); + isPageChangeEvent = true; + refresh(true); + } + } + } + + public static class PageChangeEvent { + + private final int pageNumber; + + public PageChangeEvent(int newPageNumber) { + pageNumber = newPageNumber; + } + + public int getPageNumber() { + return pageNumber; + } + } + + public static class PageCountChangeEvent { + + private final int pageCount; + + public PageCountChangeEvent(int newPageCount) { + pageCount = newPageCount; + } + + public int getPageCount() { + return pageCount; + } + } + + public static class PagingDestroyedEvent { + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KnownAndSlackFilter.java b/Core/src/org/sleuthkit/autopsy/datamodel/KnownAndSlackFilter.java new file mode 100644 index 0000000000..2d780328be --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KnownAndSlackFilter.java @@ -0,0 +1,60 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datamodel; + +import java.util.function.Predicate; +import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; + +/** + * + */ +class KnownAndSlackFilter implements Predicate { + + @Override + public boolean test(T t) { + AbstractFile af = null; + + if (t instanceof BlackboardArtifact) { + try { + af = ((BlackboardArtifact) (t)).getSleuthkitCase().getAbstractFileById(((BlackboardArtifact) t).getObjectID()); + } catch (TskCoreException ex) { + Exceptions.printStackTrace(ex); + } + } else if (t instanceof AbstractFile) { + af = (AbstractFile) t; + } + + if (af != null) { + if (af.getKnown() == TskData.FileKnown.KNOWN && UserPreferences.hideKnownFilesInViewsTree()) { + return false; + } + if (af.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) && UserPreferences.hideSlackFilesInViewsTree()) { + return false; + } + } + + return true; + } +} From 07a375bd12047f17dd6e03596e87accb4d64d2ae Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 8 Apr 2019 16:56:36 -0400 Subject: [PATCH 02/57] Moved event classes to make them accessible. --- .../corecomponents/DataResultViewerTable.java | 290 +++++++++++++++++- .../autopsy/datamodel/BaseChildFactory.java | 123 ++++---- 2 files changed, 346 insertions(+), 67 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index bf18c4b831..a6acc8f9ec 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.corecomponents; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; import java.awt.Component; import java.awt.Cursor; import java.awt.FontMetrics; @@ -26,6 +28,7 @@ import java.awt.dnd.DnDConstants; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.FeatureDescriptor; +import java.beans.PropertyChangeEvent; import java.beans.PropertyVetoException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; @@ -38,6 +41,8 @@ import java.util.TreeSet; import java.util.logging.Level; import java.util.prefs.Preferences; import javax.swing.ImageIcon; +import javax.swing.InputVerifier; +import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.ListSelectionModel; import static javax.swing.SwingConstants.CENTER; @@ -61,6 +66,10 @@ import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Node.Property; +import org.openide.nodes.NodeEvent; +import org.openide.nodes.NodeListener; +import org.openide.nodes.NodeMemberEvent; +import org.openide.nodes.NodeReorderEvent; import org.openide.util.ImageUtilities; import org.openide.util.NbBundle; import org.openide.util.NbPreferences; @@ -70,6 +79,10 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo; +import org.sleuthkit.autopsy.datamodel.BaseChildFactory; +import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageChangeEvent; +import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageCountChangeEvent; +import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PagingDestroyedEvent; /** * A tabular result viewer that displays the children of the given root node @@ -95,7 +108,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { private static final ImageIcon INTERESTING_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH, false)); private static final ImageIcon NOTABLE_ICON_SCORE = new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH, false)); @NbBundle.Messages("DataResultViewerTable.firstColLbl=Name") - static private final String FIRST_COLUMN_LABEL = Bundle.DataResultViewerTable_firstColLbl(); + static protected final String FIRST_COLUMN_LABEL = Bundle.DataResultViewerTable_firstColLbl(); private final String title; private final Map columnMap; private final Map> propertiesMap; @@ -103,6 +116,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer { private final TableListener outlineViewListener; private final IconRendererTableListener iconRendererListener; private Node rootNode; + private final Map pagingSupportMap = new HashMap<>(); + private PagingSupport pagingSupport = null; /** * Constructs a tabular result viewer that displays the children of the @@ -144,11 +159,15 @@ public class DataResultViewerTable extends AbstractDataResultViewer { this.columnMap = new HashMap<>(); this.propertiesMap = new TreeMap<>(); + this.pagingSupport = new PagingSupport(); + /* * Execute the code generated by the GUI builder. */ initComponents(); + this.pagingSupport.updateControls(); + /* * Configure the child OutlineView (explorer view) component. */ @@ -252,6 +271,40 @@ public class DataResultViewerTable extends AbstractDataResultViewer { */ if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) { this.rootNode = rootNode; + pagingSupport = pagingSupportMap.get(rootNode.getName()); + if (pagingSupport == null) { + EventBus bus = BaseChildFactory.nodeNameToEventBusMap.get(rootNode.getName()); + pagingSupport = new PagingSupport(); + pagingSupport.registerWithEventBus(bus); + pagingSupportMap.put(rootNode.getName(), pagingSupport); + } + pagingSupport.updateControls(); + + rootNode.addNodeListener(new NodeListener() { + @Override + public void childrenAdded(NodeMemberEvent nme) { + SwingUtilities.invokeLater(() -> { + setCursor(null); + }); + } + + @Override + public void childrenRemoved(NodeMemberEvent nme) { + } + + @Override + public void childrenReordered(NodeReorderEvent nre) { + } + + @Override + public void nodeDestroyed(NodeEvent ne) { + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + } + }); + this.getExplorerManager().setRootContext(this.rootNode); setupTable(); } else { @@ -665,6 +718,107 @@ public class DataResultViewerTable extends AbstractDataResultViewer { } } + private class PagingSupport { + + private int currentPage; + private int totalPages; + private EventBus eb; + + PagingSupport() { + currentPage = 1; + totalPages = 0; + eb = null; + } + + void reset() { + currentPage = 1; + totalPages = 0; + + if (eb != null) { + eb.unregister(this); + eb = null; + } + updateControls(); + } + + void registerWithEventBus(EventBus bus) { + if (eb == bus) { + return; + } + + if (bus != null) { + eb = bus; + eb.register(this); + } + + updateControls(); + } + + void nextPage() { + currentPage++; + postPageChangeEvent(); + } + + void previousPage() { + currentPage--; + postPageChangeEvent(); + } + + void gotoPage() { + currentPage = Integer.decode(gotoPageTextField.getText()); + postPageChangeEvent(); + } + + void postPageChangeEvent() { + if (eb != null) { + eb.post(new PageChangeEvent(currentPage)); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + updateControls(); + } + + boolean isValidPage() { + if (gotoPageTextField.getText().length() > 0) { + Integer value = Integer.decode(gotoPageTextField.getText()); + return value >= 1 && value <= totalPages; + } else { + return true; + } + } + + @Subscribe + public void subscribeToPageCountChange(PageCountChangeEvent event) { + if (event != null) { + totalPages = event.getPageCount(); + updateControls(); + } + } + + @Subscribe + public void subscribeToPagingDestroyedEvent(PagingDestroyedEvent event) { + if (eb != null) { + pagingSupportMap.remove(eb.identifier()); + } + } + + private void updateControls() { + if (totalPages == 0) { + pagePrevButton.setEnabled(false); + pageNextButton.setEnabled(false); + pageNumLabel.setText(""); + gotoPageTextField.setText(""); + gotoPageTextField.setEnabled(false); + } else { + pageNumLabel.setText(NbBundle.getMessage(this.getClass(), "DataResultViewerThumbnail.pageNumbers.curOfTotal", + Integer.toString(currentPage), Integer.toString(totalPages))); + + pageNextButton.setEnabled(!(currentPage == totalPages)); + pagePrevButton.setEnabled(!(currentPage == 1)); + gotoPageTextField.setEnabled(true); + } + } + } + /** * Listener which sets the custom icon renderer on columns which contain * icons instead of text when a column is added. @@ -1033,21 +1187,133 @@ public class DataResultViewerTable extends AbstractDataResultViewer { // //GEN-BEGIN:initComponents private void initComponents() { + pageLabel = new javax.swing.JLabel(); + pageNumLabel = new javax.swing.JLabel(); + pagesLabel = new javax.swing.JLabel(); + pagePrevButton = new javax.swing.JButton(); + pageNextButton = new javax.swing.JButton(); outlineView = new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL); + gotoPageLabel = new javax.swing.JLabel(); + gotoPageTextField = new javax.swing.JTextField(); + + pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageLabel.text")); // NOI18N + + pageNumLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageNumLabel.text")); // NOI18N + + pagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pagesLabel.text")); // NOI18N + + pagePrevButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N + pagePrevButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pagePrevButton.text")); // NOI18N + pagePrevButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N + pagePrevButton.setFocusable(false); + pagePrevButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + pagePrevButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); + pagePrevButton.setPreferredSize(new java.awt.Dimension(55, 23)); + pagePrevButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N + pagePrevButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + pagePrevButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pagePrevButtonActionPerformed(evt); + } + }); + + pageNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N + pageNextButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageNextButton.text")); // NOI18N + pageNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N + pageNextButton.setFocusable(false); + pageNextButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + pageNextButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); + pageNextButton.setMaximumSize(new java.awt.Dimension(27, 23)); + pageNextButton.setMinimumSize(new java.awt.Dimension(27, 23)); + pageNextButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N + pageNextButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + pageNextButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pageNextButtonActionPerformed(evt); + } + }); + + gotoPageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.gotoPageLabel.text")); // NOI18N + + gotoPageTextField.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.gotoPageTextField.text")); // NOI18N + gotoPageTextField.setInputVerifier(new InputVerifier() { + @Override + public boolean verify(JComponent input) { + return pagingSupport.isValidPage(); + }}); + gotoPageTextField.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + gotoPageTextFieldActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap(373, Short.MAX_VALUE) + .addComponent(pageLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(pagesLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(gotoPageLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) + ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {pageNextButton, pagePrevButton}); + + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) + .addComponent(pageLabel) + .addComponent(pageNumLabel) + .addComponent(pagesLabel) + .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(gotoPageLabel) + .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 324, Short.MAX_VALUE) + .addContainerGap()) + ); + + layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {pageNextButton, pagePrevButton}); + + }// //GEN-END:initComponents + + private void pagePrevButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pagePrevButtonActionPerformed + pagingSupport.previousPage(); + }//GEN-LAST:event_pagePrevButtonActionPerformed + + private void pageNextButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pageNextButtonActionPerformed + pagingSupport.nextPage(); + }//GEN-LAST:event_pageNextButtonActionPerformed + + private void gotoPageTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_gotoPageTextFieldActionPerformed + pagingSupport.gotoPage(); + }//GEN-LAST:event_gotoPageTextFieldActionPerformed - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 691, Short.MAX_VALUE) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 366, Short.MAX_VALUE) - ); - }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel gotoPageLabel; + private javax.swing.JTextField gotoPageTextField; private org.openide.explorer.view.OutlineView outlineView; + private javax.swing.JLabel pageLabel; + private javax.swing.JButton pageNextButton; + private javax.swing.JLabel pageNumLabel; + private javax.swing.JButton pagePrevButton; + private javax.swing.JLabel pagesLabel; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java index c192d88a98..44033ba0b5 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java @@ -33,8 +33,9 @@ import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.datamodel.Content; /** - * Abstract child factory that provides paging and filtering functionality - * to subclasses. + * Abstract child factory that provides paging and filtering functionality to + * subclasses. + * * @param */ public abstract class BaseChildFactory extends ChildFactory.Detachable { @@ -45,8 +46,8 @@ public abstract class BaseChildFactory extends ChildFactory.D private final PagingSupport pagingSupport; /** - * This static map is used to facilitate communication between the UI - * and the child factory. + * This static map is used to facilitate communication between the UI and + * the child factory. */ public static Map nodeNameToEventBusMap = new ConcurrentHashMap<>(); @@ -59,9 +60,9 @@ public abstract class BaseChildFactory extends ChildFactory.D @Override protected void addNotify() { onAdd(); - pagingSupport.initialize(); + pagingSupport.initialize(); } - + @Override protected void removeNotify() { onRemove(); @@ -70,7 +71,8 @@ public abstract class BaseChildFactory extends ChildFactory.D /** * Subclasses implement this to construct a collection of keys. - * @return + * + * @return */ protected abstract List makeKeys(); @@ -78,10 +80,10 @@ public abstract class BaseChildFactory extends ChildFactory.D * Subclasses implement this to initialize any required resources. */ protected abstract void onAdd(); - + /** - * Subclasses implement this to clean up any resources they - * acquired in onAdd() + * Subclasses implement this to clean up any resources they acquired in + * onAdd() */ protected abstract void onRemove(); @@ -94,7 +96,7 @@ public abstract class BaseChildFactory extends ChildFactory.D // Filter keys allKeys.stream().filter(filter).collect(Collectors.toList()); - + pagingSupport.splitKeysIntoPages(allKeys); } @@ -107,8 +109,45 @@ public abstract class BaseChildFactory extends ChildFactory.D } /** - * Class that supplies paging related functionality to the base - * child factory class. + * Event used to let subscribers know that the user has + * navigated to a different page. + */ + public static class PageChangeEvent { + + private final int pageNumber; + + public PageChangeEvent(int newPageNumber) { + pageNumber = newPageNumber; + } + + public int getPageNumber() { + return pageNumber; + } + } + + /** + * Event used to let subscribers know that the number of + * pages has changed. + */ + public static class PageCountChangeEvent { + + private final int pageCount; + + public PageCountChangeEvent(int newPageCount) { + pageCount = newPageCount; + } + + public int getPageCount() { + return pageCount; + } + } + + public static class PagingDestroyedEvent { + } + + /** + * Class that supplies paging related functionality to the base child + * factory class. */ class PagingSupport { @@ -120,9 +159,11 @@ public abstract class BaseChildFactory extends ChildFactory.D /** * Construct PagingSupport instance for the given node name. - * @param nodeName Name of the node in the tree for which results - * are being displayed. The node name is used to allow communication - * between the UI and the ChildFactory via an EventBus. + * + * @param nodeName Name of the node in the tree for which results are + * being displayed. The node name is used to allow + * communication between the UI and the ChildFactory via + * an EventBus. */ PagingSupport(String nodeName) { pageSize = UserPreferences.getResultsTablePageSize(); @@ -130,14 +171,14 @@ public abstract class BaseChildFactory extends ChildFactory.D pages = new ArrayList<>(); this.nodeName = nodeName; } - + void initialize() { if (pageSize > 0) { // Only configure an EventBus if paging functionality is enabled. bus = new EventBus(nodeName); nodeNameToEventBusMap.put(bus.identifier(), bus); bus.register(this); - } + } } void destroy() { @@ -146,31 +187,33 @@ public abstract class BaseChildFactory extends ChildFactory.D bus.unregister(this); bus.post(new PagingDestroyedEvent()); bus = null; - } + } } - + /** * Get the list of keys at the current page. + * * @return List of keys. */ List getCurrentPage() { if (pages.size() > 0) { return pages.get(currentPage - 1); } - + return Collections.emptyList(); } - + /** * Split the given collection of keys into pages based on page size. - * @param keys + * + * @param keys */ void splitKeysIntoPages(List keys) { int oldPageCount = pages.size(); /** - * If pageSize is set split keys into pages, - * otherwise create a single page containing all keys. + * If pageSize is set split keys into pages, otherwise create a + * single page containing all keys. */ pages = Lists.partition(keys, pageSize > 0 ? pageSize : keys.size()); if (pages.size() != oldPageCount) { @@ -190,34 +233,4 @@ public abstract class BaseChildFactory extends ChildFactory.D } } } - - public static class PageChangeEvent { - - private final int pageNumber; - - public PageChangeEvent(int newPageNumber) { - pageNumber = newPageNumber; - } - - public int getPageNumber() { - return pageNumber; - } - } - - public static class PageCountChangeEvent { - - private final int pageCount; - - public PageCountChangeEvent(int newPageCount) { - pageCount = newPageCount; - } - - public int getPageCount() { - return pageCount; - } - } - - public static class PagingDestroyedEvent { - - } } From 81e0823ba8e19bf044c348ab51aab9eb329bc462 Mon Sep 17 00:00:00 2001 From: esaunders Date: Tue, 9 Apr 2019 16:47:37 -0400 Subject: [PATCH 03/57] Reverted DataResultViewerTable to original state and added a comment to KnownAndSlackFilter. --- .../corecomponents/DataResultViewerTable.java | 290 +----------------- .../datamodel/KnownAndSlackFilter.java | 3 +- 2 files changed, 14 insertions(+), 279 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index a6acc8f9ec..bf18c4b831 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -18,8 +18,6 @@ */ package org.sleuthkit.autopsy.corecomponents; -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; import java.awt.Component; import java.awt.Cursor; import java.awt.FontMetrics; @@ -28,7 +26,6 @@ import java.awt.dnd.DnDConstants; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.FeatureDescriptor; -import java.beans.PropertyChangeEvent; import java.beans.PropertyVetoException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; @@ -41,8 +38,6 @@ import java.util.TreeSet; import java.util.logging.Level; import java.util.prefs.Preferences; import javax.swing.ImageIcon; -import javax.swing.InputVerifier; -import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.ListSelectionModel; import static javax.swing.SwingConstants.CENTER; @@ -66,10 +61,6 @@ import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Node.Property; -import org.openide.nodes.NodeEvent; -import org.openide.nodes.NodeListener; -import org.openide.nodes.NodeMemberEvent; -import org.openide.nodes.NodeReorderEvent; import org.openide.util.ImageUtilities; import org.openide.util.NbBundle; import org.openide.util.NbPreferences; @@ -79,10 +70,6 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo; -import org.sleuthkit.autopsy.datamodel.BaseChildFactory; -import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageChangeEvent; -import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageCountChangeEvent; -import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PagingDestroyedEvent; /** * A tabular result viewer that displays the children of the given root node @@ -108,7 +95,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { private static final ImageIcon INTERESTING_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH, false)); private static final ImageIcon NOTABLE_ICON_SCORE = new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH, false)); @NbBundle.Messages("DataResultViewerTable.firstColLbl=Name") - static protected final String FIRST_COLUMN_LABEL = Bundle.DataResultViewerTable_firstColLbl(); + static private final String FIRST_COLUMN_LABEL = Bundle.DataResultViewerTable_firstColLbl(); private final String title; private final Map columnMap; private final Map> propertiesMap; @@ -116,8 +103,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer { private final TableListener outlineViewListener; private final IconRendererTableListener iconRendererListener; private Node rootNode; - private final Map pagingSupportMap = new HashMap<>(); - private PagingSupport pagingSupport = null; /** * Constructs a tabular result viewer that displays the children of the @@ -159,15 +144,11 @@ public class DataResultViewerTable extends AbstractDataResultViewer { this.columnMap = new HashMap<>(); this.propertiesMap = new TreeMap<>(); - this.pagingSupport = new PagingSupport(); - /* * Execute the code generated by the GUI builder. */ initComponents(); - this.pagingSupport.updateControls(); - /* * Configure the child OutlineView (explorer view) component. */ @@ -271,40 +252,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer { */ if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) { this.rootNode = rootNode; - pagingSupport = pagingSupportMap.get(rootNode.getName()); - if (pagingSupport == null) { - EventBus bus = BaseChildFactory.nodeNameToEventBusMap.get(rootNode.getName()); - pagingSupport = new PagingSupport(); - pagingSupport.registerWithEventBus(bus); - pagingSupportMap.put(rootNode.getName(), pagingSupport); - } - pagingSupport.updateControls(); - - rootNode.addNodeListener(new NodeListener() { - @Override - public void childrenAdded(NodeMemberEvent nme) { - SwingUtilities.invokeLater(() -> { - setCursor(null); - }); - } - - @Override - public void childrenRemoved(NodeMemberEvent nme) { - } - - @Override - public void childrenReordered(NodeReorderEvent nre) { - } - - @Override - public void nodeDestroyed(NodeEvent ne) { - } - - @Override - public void propertyChange(PropertyChangeEvent evt) { - } - }); - this.getExplorerManager().setRootContext(this.rootNode); setupTable(); } else { @@ -718,107 +665,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer { } } - private class PagingSupport { - - private int currentPage; - private int totalPages; - private EventBus eb; - - PagingSupport() { - currentPage = 1; - totalPages = 0; - eb = null; - } - - void reset() { - currentPage = 1; - totalPages = 0; - - if (eb != null) { - eb.unregister(this); - eb = null; - } - updateControls(); - } - - void registerWithEventBus(EventBus bus) { - if (eb == bus) { - return; - } - - if (bus != null) { - eb = bus; - eb.register(this); - } - - updateControls(); - } - - void nextPage() { - currentPage++; - postPageChangeEvent(); - } - - void previousPage() { - currentPage--; - postPageChangeEvent(); - } - - void gotoPage() { - currentPage = Integer.decode(gotoPageTextField.getText()); - postPageChangeEvent(); - } - - void postPageChangeEvent() { - if (eb != null) { - eb.post(new PageChangeEvent(currentPage)); - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - } - updateControls(); - } - - boolean isValidPage() { - if (gotoPageTextField.getText().length() > 0) { - Integer value = Integer.decode(gotoPageTextField.getText()); - return value >= 1 && value <= totalPages; - } else { - return true; - } - } - - @Subscribe - public void subscribeToPageCountChange(PageCountChangeEvent event) { - if (event != null) { - totalPages = event.getPageCount(); - updateControls(); - } - } - - @Subscribe - public void subscribeToPagingDestroyedEvent(PagingDestroyedEvent event) { - if (eb != null) { - pagingSupportMap.remove(eb.identifier()); - } - } - - private void updateControls() { - if (totalPages == 0) { - pagePrevButton.setEnabled(false); - pageNextButton.setEnabled(false); - pageNumLabel.setText(""); - gotoPageTextField.setText(""); - gotoPageTextField.setEnabled(false); - } else { - pageNumLabel.setText(NbBundle.getMessage(this.getClass(), "DataResultViewerThumbnail.pageNumbers.curOfTotal", - Integer.toString(currentPage), Integer.toString(totalPages))); - - pageNextButton.setEnabled(!(currentPage == totalPages)); - pagePrevButton.setEnabled(!(currentPage == 1)); - gotoPageTextField.setEnabled(true); - } - } - } - /** * Listener which sets the custom icon renderer on columns which contain * icons instead of text when a column is added. @@ -1187,133 +1033,21 @@ public class DataResultViewerTable extends AbstractDataResultViewer { // //GEN-BEGIN:initComponents private void initComponents() { - pageLabel = new javax.swing.JLabel(); - pageNumLabel = new javax.swing.JLabel(); - pagesLabel = new javax.swing.JLabel(); - pagePrevButton = new javax.swing.JButton(); - pageNextButton = new javax.swing.JButton(); outlineView = new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL); - gotoPageLabel = new javax.swing.JLabel(); - gotoPageTextField = new javax.swing.JTextField(); - - pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageLabel.text")); // NOI18N - - pageNumLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageNumLabel.text")); // NOI18N - - pagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pagesLabel.text")); // NOI18N - - pagePrevButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N - pagePrevButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pagePrevButton.text")); // NOI18N - pagePrevButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N - pagePrevButton.setFocusable(false); - pagePrevButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - pagePrevButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); - pagePrevButton.setPreferredSize(new java.awt.Dimension(55, 23)); - pagePrevButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N - pagePrevButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - pagePrevButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - pagePrevButtonActionPerformed(evt); - } - }); - - pageNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N - pageNextButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageNextButton.text")); // NOI18N - pageNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N - pageNextButton.setFocusable(false); - pageNextButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - pageNextButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); - pageNextButton.setMaximumSize(new java.awt.Dimension(27, 23)); - pageNextButton.setMinimumSize(new java.awt.Dimension(27, 23)); - pageNextButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N - pageNextButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - pageNextButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - pageNextButtonActionPerformed(evt); - } - }); - - gotoPageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.gotoPageLabel.text")); // NOI18N - - gotoPageTextField.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.gotoPageTextField.text")); // NOI18N - gotoPageTextField.setInputVerifier(new InputVerifier() { - @Override - public boolean verify(JComponent input) { - return pagingSupport.isValidPage(); - }}); - gotoPageTextField.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - gotoPageTextFieldActionPerformed(evt); - } - }); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap(373, Short.MAX_VALUE) - .addComponent(pageLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(pagesLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(gotoPageLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) - ); - - layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {pageNextButton, pagePrevButton}); - - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) - .addComponent(pageLabel) - .addComponent(pageNumLabel) - .addComponent(pagesLabel) - .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(gotoPageLabel) - .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 324, Short.MAX_VALUE) - .addContainerGap()) - ); - - layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {pageNextButton, pagePrevButton}); - - }// //GEN-END:initComponents - - private void pagePrevButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pagePrevButtonActionPerformed - pagingSupport.previousPage(); - }//GEN-LAST:event_pagePrevButtonActionPerformed - - private void pageNextButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pageNextButtonActionPerformed - pagingSupport.nextPage(); - }//GEN-LAST:event_pageNextButtonActionPerformed - - private void gotoPageTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_gotoPageTextFieldActionPerformed - pagingSupport.gotoPage(); - }//GEN-LAST:event_gotoPageTextFieldActionPerformed + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 691, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 366, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JLabel gotoPageLabel; - private javax.swing.JTextField gotoPageTextField; private org.openide.explorer.view.OutlineView outlineView; - private javax.swing.JLabel pageLabel; - private javax.swing.JButton pageNextButton; - private javax.swing.JLabel pageNumLabel; - private javax.swing.JButton pagePrevButton; - private javax.swing.JLabel pagesLabel; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KnownAndSlackFilter.java b/Core/src/org/sleuthkit/autopsy/datamodel/KnownAndSlackFilter.java index 2d780328be..5c97a5a044 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KnownAndSlackFilter.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KnownAndSlackFilter.java @@ -28,7 +28,8 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; /** - * + * Predicate that can be used to filter known and/or slack files from + * Content collections based on user preferences. */ class KnownAndSlackFilter implements Predicate { From 7b1cb3e1d156f5f73436190136049a934eed412a Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 10 Apr 2019 17:45:05 -0400 Subject: [PATCH 04/57] Codacy fixes. --- .../org/sleuthkit/autopsy/datamodel/BaseChildFactory.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java index 44033ba0b5..8c97208c04 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java @@ -53,6 +53,7 @@ public abstract class BaseChildFactory extends ChildFactory.D public BaseChildFactory(String nodeName) { pagingSupport = new PagingSupport(nodeName); + pagingSupport.initialize(); isPageChangeEvent = false; filter = new KnownAndSlackFilter<>(); } @@ -60,7 +61,6 @@ public abstract class BaseChildFactory extends ChildFactory.D @Override protected void addNotify() { onAdd(); - pagingSupport.initialize(); } @Override @@ -142,6 +142,10 @@ public abstract class BaseChildFactory extends ChildFactory.D } } + /** + * Event used to let subscribers know that paging is no + * longer required. + */ public static class PagingDestroyedEvent { } @@ -196,7 +200,7 @@ public abstract class BaseChildFactory extends ChildFactory.D * @return List of keys. */ List getCurrentPage() { - if (pages.size() > 0) { + if (pages.isEmpty()) { return pages.get(currentPage - 1); } From f54c9aa2ffb0f7252333933a4b73e0c36ff612b8 Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 17 Apr 2019 11:48:16 -0400 Subject: [PATCH 05/57] Removed PagingSupport.destroy() and fixed bug in PagingSupport.getCurrentPage(). --- .../autopsy/datamodel/BaseChildFactory.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java index 8c97208c04..555d59b33c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java @@ -66,7 +66,6 @@ public abstract class BaseChildFactory extends ChildFactory.D @Override protected void removeNotify() { onRemove(); - pagingSupport.destroy(); } /** @@ -179,28 +178,23 @@ public abstract class BaseChildFactory extends ChildFactory.D void initialize() { if (pageSize > 0) { // Only configure an EventBus if paging functionality is enabled. - bus = new EventBus(nodeName); - nodeNameToEventBusMap.put(bus.identifier(), bus); + if (nodeNameToEventBusMap.containsKey(nodeName)) { + bus = nodeNameToEventBusMap.get(nodeName); + } else { + bus = new EventBus(nodeName); + nodeNameToEventBusMap.put(bus.identifier(), bus); + } bus.register(this); } } - void destroy() { - if (bus != null) { - nodeNameToEventBusMap.remove(bus.identifier()); - bus.unregister(this); - bus.post(new PagingDestroyedEvent()); - bus = null; - } - } - /** * Get the list of keys at the current page. * * @return List of keys. */ List getCurrentPage() { - if (pages.isEmpty()) { + if (!pages.isEmpty()) { return pages.get(currentPage - 1); } From c4e81f2ea25e02d4aedcb077ab357dd4ecb4a1f8 Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 17 Apr 2019 13:05:50 -0400 Subject: [PATCH 06/57] Converted DeletedContent, ExtractedContent, FileSize, FileTypesByExtension, FileTypesByMimeType, HashsetHits, InterestingHits and KeywordHits to new child factory approach that supports paging. --- .../autopsy/datamodel/DeletedContent.java | 26 +- .../autopsy/datamodel/ExtractedContent.java | 40 +-- .../sleuthkit/autopsy/datamodel/FileSize.java | 31 +-- .../datamodel/FileTypesByExtension.java | 35 +-- .../datamodel/FileTypesByMimeType.java | 38 ++- .../autopsy/datamodel/HashsetHits.java | 61 ++-- .../autopsy/datamodel/InterestingHits.java | 52 ++-- .../autopsy/datamodel/KeywordHits.java | 260 +++++++++--------- 8 files changed, 273 insertions(+), 270 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java index 4e81d4b428..435364803d 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,7 +39,6 @@ import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.AbstractFile; @@ -358,7 +357,7 @@ public class DeletedContent implements AutopsyVisitableItem { } } - static class DeletedContentChildren extends ChildFactory.Detachable { + static class DeletedContentChildren extends BaseChildFactory { private final SleuthkitCase skCase; private final DeletedContent.DeletedContentFilter filter; @@ -368,6 +367,7 @@ public class DeletedContent implements AutopsyVisitableItem { private final long datasourceObjId; DeletedContentChildren(DeletedContent.DeletedContentFilter filter, SleuthkitCase skCase, Observable o, long datasourceObjId) { + super(filter.getName()); this.skCase = skCase; this.filter = filter; this.notifier = o; @@ -376,6 +376,11 @@ public class DeletedContent implements AutopsyVisitableItem { private final Observer observer = new DeletedContentChildrenObserver(); + @Override + protected List makeKeys() { + return runFsQuery(); + } + // Cause refresh of children if there are changes private class DeletedContentChildrenObserver implements Observer { @@ -386,25 +391,19 @@ public class DeletedContent implements AutopsyVisitableItem { } @Override - protected void addNotify() { + protected void onAdd() { if (notifier != null) { notifier.addObserver(observer); } } @Override - protected void removeNotify() { + protected void onRemove() { if (notifier != null) { notifier.deleteObserver(observer); } } - @Override - protected boolean createKeys(List list) { - list.addAll(runFsQuery()); - return true; - } - static private String makeQuery(DeletedContent.DeletedContentFilter filter, long filteringDSObjId) { String query = ""; switch (filter) { @@ -440,11 +439,6 @@ public class DeletedContent implements AutopsyVisitableItem { } - if (UserPreferences.hideKnownFilesInViewsTree()) { - query += " AND (known != " + TskData.FileKnown.KNOWN.getFileKnownValue() //NON-NLS - + " OR known IS NULL)"; //NON-NLS - } - if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { query += " AND data_source_obj_id = " + filteringDSObjId; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index 4b3694b35f..9844811a02 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -423,12 +423,12 @@ public class ExtractedContent implements AutopsyVisitableItem { /** * Creates children for a given artifact type */ - private class ArtifactFactory extends ChildFactory.Detachable { + private class ArtifactFactory extends BaseChildFactory { private BlackboardArtifact.Type type; public ArtifactFactory(BlackboardArtifact.Type type) { - super(); + super(type.getTypeName()); this.type = type; } @@ -481,36 +481,36 @@ public class ExtractedContent implements AutopsyVisitableItem { }; @Override - protected void addNotify() { + protected void onAdd() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); } @Override - protected void removeNotify() { + protected void onRemove() { IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestModuleEventListener(pcl); } - @Override - protected boolean createKeys(List list) { - if (skCase != null) { - try { - List arts = - Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ? - blackboard.getArtifacts(type.getTypeID(), datasourceObjId) : - skCase.getBlackboardArtifacts(type.getTypeID()); - list.addAll(arts); - } catch (TskException ex) { - Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS - } - } - return true; - } - @Override protected Node createNodeForKey(BlackboardArtifact key) { return new BlackboardArtifactNode(key); } + + @Override + protected List makeKeys() { + if (skCase != null) { + try { + List arts = + Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ? + blackboard.getArtifacts(type.getTypeID(), datasourceObjId) : + skCase.getBlackboardArtifacts(type.getTypeID()); + return arts; + } catch (TskException ex) { + Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS + } + } + return Collections.emptyList(); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java index 53628326a2..78d38259f3 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2018 Basis Technology Corp. + * Copyright 2013-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -361,7 +361,7 @@ public class FileSize implements AutopsyVisitableItem { /* * Makes children, which are nodes for files of a given range */ - static class FileSizeChildren extends ChildFactory.Detachable { + static class FileSizeChildren extends BaseChildFactory { private final SleuthkitCase skCase; private final FileSizeFilter filter; @@ -377,6 +377,7 @@ public class FileSize implements AutopsyVisitableItem { * added to case */ FileSizeChildren(FileSizeFilter filter, SleuthkitCase skCase, Observable o, long dsObjId) { + super(filter.getName()); this.skCase = skCase; this.filter = filter; this.notifier = o; @@ -385,14 +386,14 @@ public class FileSize implements AutopsyVisitableItem { } @Override - protected void addNotify() { + protected void onAdd() { if (notifier != null) { notifier.addObserver(observer); } } @Override - protected void removeNotify() { + protected void onRemove() { if (notifier != null) { notifier.deleteObserver(observer); } @@ -400,6 +401,11 @@ public class FileSize implements AutopsyVisitableItem { private final Observer observer = new FileSizeChildrenObserver(); + @Override + protected List makeKeys() { + return runFsQuery(); + } + // Cause refresh of children if there are changes private class FileSizeChildrenObserver implements Observer { @@ -409,12 +415,6 @@ public class FileSize implements AutopsyVisitableItem { } } - @Override - protected boolean createKeys(List list) { - list.addAll(runFsQuery()); - return true; - } - private static String makeQuery(FileSizeFilter filter, long filteringDSObjId) { String query; switch (filter) { @@ -436,17 +436,6 @@ public class FileSize implements AutopsyVisitableItem { // Ignore unallocated block files. query = query + " AND (type != " + TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType() + ")"; //NON-NLS - // Hide known files if indicated in the user preferences. - if(UserPreferences.hideKnownFilesInViewsTree()) { - query += " AND (known != " + TskData.FileKnown.KNOWN.getFileKnownValue() //NON-NLS - + " OR known IS NULL)"; //NON-NLS - } - - // Hide slack files if indicated in the user preferences. - if(UserPreferences.hideSlackFilesInViewsTree()) { - query += " AND (type != " + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType() + ")"; //NON-NLS - } - // filter by datasource if indicated in case preferences if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { query += " AND data_source_obj_id = " + filteringDSObjId; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 5f1f33607d..27e0705d08 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datamodel; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Arrays; +import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Objects; @@ -292,7 +293,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { * should refresh */ FileExtensionNode(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, FileTypesByExtObservable o) { - super(typesRoot, Children.create(new FileExtensionNodeChildren(filter, skCase, o), true), + super(typesRoot, Children.create(new FileExtensionNodeChildren(filter, skCase, o, filter.getDisplayName()), true), Lookups.singleton(filter.getDisplayName())); this.filter = filter; super.setName(filter.getDisplayName()); @@ -377,7 +378,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { /** * Child node factory for a specific file type - does the database query. */ - private class FileExtensionNodeChildren extends ChildFactory.Detachable implements Observer { + private class FileExtensionNodeChildren extends BaseChildFactory implements Observer { private final SleuthkitCase skCase; private final FileTypesByExtension.SearchFilterInterface filter; @@ -390,22 +391,22 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { * @param o Observable that will notify when there could be new * data to display */ - private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { - super(); + private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o, String nodeName) { + super(nodeName); this.filter = filter; this.skCase = skCase; notifier = o; } @Override - protected void addNotify() { + protected void onAdd() { if (notifier != null) { notifier.addObserver(this); } } @Override - protected void removeNotify() { + protected void onRemove() { if (notifier != null) { notifier.deleteObserver(this); } @@ -417,19 +418,19 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { } @Override - protected boolean createKeys(List list) { - try { - list.addAll(skCase.findAllFilesWhere(createQuery(filter)) - .stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList())); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS - } - return true; + protected Node createNodeForKey(FileTypesKey key) { + return key.accept(new FileTypes.FileNodeCreationVisitor()); } @Override - protected Node createNodeForKey(FileTypesKey key) { - return key.accept(new FileTypes.FileNodeCreationVisitor()); + protected List makeKeys() { + try { + return skCase.findAllFilesWhere(createQuery(filter)) + .stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList()); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS + } + return Collections.emptyList(); } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 54bb2e7285..49f586c7e8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -445,27 +445,16 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi * files that match MimeType which is represented by this position in the * tree. */ - private class MediaSubTypeNodeChildren extends ChildFactory.Detachable implements Observer { + private class MediaSubTypeNodeChildren extends BaseChildFactory implements Observer { private final String mimeType; private MediaSubTypeNodeChildren(String mimeType) { - super(); + super(mimeType); addObserver(this); this.mimeType = mimeType; } - @Override - protected boolean createKeys(List list) { - try { - list.addAll(skCase.findAllFilesWhere(createBaseWhereExpr() + " AND mime_type = '" + mimeType + "'") - .stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList())); //NON-NLS - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS - } - return true; - } - @Override public void update(Observable o, Object arg) { refresh(true); @@ -475,5 +464,26 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi protected Node createNodeForKey(FileTypesKey key) { return key.accept(new FileTypes.FileNodeCreationVisitor()); } + + @Override + protected List makeKeys() { + try { + return skCase.findAllFilesWhere(createBaseWhereExpr() + " AND mime_type = '" + mimeType + "'") + .stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList()); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS + } + return Collections.emptyList(); + } + + @Override + protected void onAdd() { + // No-op + } + + @Override + protected void onRemove() { + // No-op + } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java index 3262087487..f970585455 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,6 +35,7 @@ import java.util.Observable; import java.util.Observer; import java.util.Set; import java.util.logging.Level; +import java.util.stream.Collectors; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -44,7 +45,6 @@ import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; @@ -378,60 +378,53 @@ public class HashsetHits implements AutopsyVisitableItem { /** * Creates the nodes for the hits in a given set. */ - private class HitFactory extends ChildFactory.Detachable implements Observer { + private class HitFactory extends BaseChildFactory implements Observer { private String hashsetName; private Map artifactHits = new HashMap<>(); private HitFactory(String hashsetName) { - super(); + super(hashsetName); this.hashsetName = hashsetName; } @Override - protected void addNotify() { + protected void onAdd() { hashsetResults.addObserver(this); } @Override - protected void removeNotify() { + protected void onRemove() { hashsetResults.deleteObserver(this); } @Override - protected boolean createKeys(List list) { - - if (skCase == null) { - return true; - } - - hashsetResults.getArtifactIds(hashsetName).forEach((id) -> { - try { - if (!artifactHits.containsKey(id)) { - BlackboardArtifact art = skCase.getBlackboardArtifact(id); - artifactHits.put(id, art); - } - } catch (TskException ex) { - logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS - } - }); - - // Adding all keys at once is more efficient than adding one at a - // time because Netbeans triggers internal processing each time an - // element is added to the list. - list.addAll(artifactHits.keySet()); - return true; - } - - @Override - protected Node createNodeForKey(Long id) { - BlackboardArtifact art = artifactHits.get(id); - return (null == art) ? null : new BlackboardArtifactNode(art); + protected Node createNodeForKey(BlackboardArtifact key) { + return new BlackboardArtifactNode(key); } @Override public void update(Observable o, Object arg) { refresh(true); } + + @Override + protected List makeKeys() { + if (skCase != null) { + + hashsetResults.getArtifactIds(hashsetName).forEach((id) -> { + try { + if (!artifactHits.containsKey(id)) { + BlackboardArtifact art = skCase.getBlackboardArtifact(id); + artifactHits.put(id, art); + } + } catch (TskException ex) { + logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS + } + }); + return new ArrayList<>(artifactHits.values()); + } + return Collections.emptyList(); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java index 8782b0fb89..c36833992c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -449,51 +449,57 @@ public class InterestingHits implements AutopsyVisitableItem { } } - private class HitFactory extends ChildFactory implements Observer { + private class HitFactory extends BaseChildFactory implements Observer { private final String setName; private final String typeName; private final Map artifactHits = new HashMap<>(); private HitFactory(String setName, String typeName) { - super(); + super(typeName); this.setName = setName; this.typeName = typeName; interestingResults.addObserver(this); } @Override - protected boolean createKeys(List list) { + protected List makeKeys() { - if (skCase == null) { - return true; - } - - interestingResults.getArtifactIds(setName, typeName).forEach((id) -> { - try { - if (!artifactHits.containsKey(id)) { - BlackboardArtifact art = skCase.getBlackboardArtifact(id); - artifactHits.put(id, art); + if (skCase != null) { + interestingResults.getArtifactIds(setName, typeName).forEach((id) -> { + try { + if (!artifactHits.containsKey(id)) { + BlackboardArtifact art = skCase.getBlackboardArtifact(id); + artifactHits.put(id, art); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS - } - }); + }); - list.addAll(artifactHits.keySet()); - - return true; + return new ArrayList<>(artifactHits.values()); + } + return Collections.emptyList(); } @Override - protected Node createNodeForKey(Long l) { - BlackboardArtifact art = artifactHits.get(l); - return (null == art) ? null : new BlackboardArtifactNode(art); + protected Node createNodeForKey(BlackboardArtifact art) { + return new BlackboardArtifactNode(art); } @Override public void update(Observable o, Object arg) { refresh(true); } + + @Override + protected void onAdd() { + // No-op + } + + @Override + protected void onRemove() { + // No-op + } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index c80069c7ef..cc6d88da29 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,6 +25,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -34,7 +35,6 @@ import java.util.Observable; import java.util.Observer; import java.util.Set; import java.util.logging.Level; -import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; @@ -46,7 +46,6 @@ import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import static org.sleuthkit.autopsy.datamodel.Bundle.*; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -85,7 +84,6 @@ public class KeywordHits implements AutopsyVisitableItem { */ private static final String DEFAULT_INSTANCE_NAME = "DEFAULT_INSTANCE_NAME"; - /** * query attributes table for the ones that we need for the tree */ @@ -108,20 +106,20 @@ public class KeywordHits implements AutopsyVisitableItem { /** * Constructor - * - * @param skCase Case DB - */ + * + * @param skCase Case DB + */ KeywordHits(SleuthkitCase skCase) { this(skCase, 0); } - + /** * Constructor - * - * @param skCase Case DB - * @param objId Object id of the data source - * - */ + * + * @param skCase Case DB + * @param objId Object id of the data source + * + */ public KeywordHits(SleuthkitCase skCase, long objId) { this.skCase = skCase; this.datasourceObjId = objId; @@ -324,9 +322,9 @@ public class KeywordHits implements AutopsyVisitableItem { String queryStr = KEYWORD_HIT_ATTRIBUTES_QUERY; if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { - queryStr += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId; + queryStr += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId; } - + try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) { ResultSet resultSet = dbQuery.getResultSet(); while (resultSet.next()) { @@ -530,7 +528,7 @@ public class KeywordHits implements AutopsyVisitableItem { } final void updateDisplayName() { - super.setDisplayName(getName() + " (" + countTotalDescendants() + ")"); + super.setDisplayName(getDisplayName() + " (" + countTotalDescendants() + ")"); } abstract int countTotalDescendants(); @@ -631,6 +629,24 @@ public class KeywordHits implements AutopsyVisitableItem { } } + /** + * Create a ChildFactory object for the given set name and keyword. + * + * The type of ChildFactory we create is based on whether the node + * represents a regular expression keyword search or not. For regular + * expression keyword searches there will be an extra layer in the tree that + * represents each of the individual terms found by the regular expression. + * E.g., for an email regular expression search there will be a node in the + * tree for every email address hit. + */ + ChildFactory createChildFactory(String setName, String keyword) { + if (isOnlyDefaultInstance(keywordResults.getKeywordInstances(setName, keyword))) { + return new HitsFactory(setName, keyword, DEFAULT_INSTANCE_NAME); + } else { + return new RegExpInstancesFactory(setName, keyword); + } + } + /** * Represents the search term or regexp that user searched for */ @@ -640,8 +656,17 @@ public class KeywordHits implements AutopsyVisitableItem { private final String keyword; private TermNode(String setName, String keyword) { - super(Children.create(new RegExpInstancesFactory(setName, keyword), true), Lookups.singleton(keyword)); - super.setName(keyword); + super(Children.create(createChildFactory(setName, keyword), true), Lookups.singleton(keyword)); + + super.setDisplayName(keyword); + /** + * We differentiate between the programmatic name and the display + * name. The programmatic name is used to create an association with + * an event bus and must be the same as the node name passed by our + * ChildFactory to it's parent constructor. See the HitsFactory + * constructor for an example. + */ + super.setName(setName + "_" + keyword); this.setName = setName; this.keyword = keyword; this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS @@ -694,45 +719,11 @@ public class KeywordHits implements AutopsyVisitableItem { } } - /** - * Allows us to pass in either longs or strings as they keys for different - * types of nodes at the same level. Probably a better way to do this, but - * it works. - */ - private class RegExpInstanceKey { - - private final boolean isRegExp; - private String strKey; - private Long longKey; - - RegExpInstanceKey(String key) { - isRegExp = true; - strKey = key; - } - - RegExpInstanceKey(Long key) { - isRegExp = false; - longKey = key; - } - - boolean isRegExp() { - return isRegExp; - } - - Long getIdKey() { - return longKey; - } - - String getRegExpKey() { - return strKey; - } - } - /** * Creates the nodes for a given regexp that represent the specific terms * that were found */ - private class RegExpInstancesFactory extends DetachableObserverChildFactory { + private class RegExpInstancesFactory extends DetachableObserverChildFactory { private final String keyword; private final String setName; @@ -744,33 +735,15 @@ public class KeywordHits implements AutopsyVisitableItem { } @Override - protected boolean createKeys(List list) { - List instances = keywordResults.getKeywordInstances(setName, keyword); - // The keys are different depending on what we are displaying. - // regexp get another layer to show instances. - // Exact/substring matches don't. - if (isOnlyDefaultInstance(instances)) { - list.addAll(keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME).stream() - .map(RegExpInstanceKey::new) - .collect(Collectors.toList())); - } else { - list.addAll(instances.stream() - .map(RegExpInstanceKey::new) - .collect(Collectors.toList())); - } + protected boolean createKeys(List list) { + list.addAll(keywordResults.getKeywordInstances(setName, keyword)); return true; } @Override - protected Node createNodeForKey(RegExpInstanceKey key) { - if (key.isRegExp()) { - return new RegExpInstanceNode(setName, keyword, key.getRegExpKey()); - } else { - // if it isn't a regexp, then skip the 'instance' layer of the tree - return createBlackboardArtifactNode(key.getIdKey()); - } + protected Node createNodeForKey(String key) { + return new RegExpInstanceNode(setName, keyword, key); } - } /** @@ -784,7 +757,15 @@ public class KeywordHits implements AutopsyVisitableItem { private RegExpInstanceNode(String setName, String keyword, String instance) { super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(instance)); - super.setName(instance); //the instance represents the name of the keyword hit at this point as the keyword is the regex + super.setDisplayName(instance); //the instance represents the name of the keyword hit at this point as the keyword is the regex + /** + * We differentiate between the programmatic name and the display + * name. The programmatic name is used to create an association with + * an event bus and must be the same as the node name passed by our + * ChildFactory to it's parent constructor. See the HitsFactory + * constructor for an example. + */ + super.setName(setName + "_" + keyword + "_" + instance); this.setName = setName; this.keyword = keyword; this.instance = instance; @@ -837,7 +818,7 @@ public class KeywordHits implements AutopsyVisitableItem { /** * Create a blackboard node for the given Keyword Hit artifact * - * @param artifactId + * @param art * * @return Node or null on error */ @@ -850,81 +831,110 @@ public class KeywordHits implements AutopsyVisitableItem { "KeywordHits.createNodeForKey.chgTime.name=ChangeTime", "KeywordHits.createNodeForKey.chgTime.displayName=Change Time", "KeywordHits.createNodeForKey.chgTime.desc=Change Time"}) - private BlackboardArtifactNode createBlackboardArtifactNode(Long artifactId) { + private BlackboardArtifactNode createBlackboardArtifactNode(BlackboardArtifact art) { if (skCase == null) { return null; } - try { - BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId); - BlackboardArtifactNode n = new BlackboardArtifactNode(art); - // The associated file should be available through the Lookup that - // gets created when the BlackboardArtifactNode is constructed. - AbstractFile file = n.getLookup().lookup(AbstractFile.class); - if (file == null) { - try { - file = skCase.getAbstractFileById(art.getObjectID()); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex); //NON-NLS - return n; - } - } - /* - * It is possible to get a keyword hit on artifacts generated for - * the underlying image in which case MAC times are not - * available/applicable/useful. - */ - if (file == null) { + BlackboardArtifactNode n = new BlackboardArtifactNode(art); //NON-NLS + + // The associated file should be available through the Lookup that + // gets created when the BlackboardArtifactNode is constructed. + AbstractFile file = n.getLookup().lookup(AbstractFile.class); + if (file == null) { + try { + file = skCase.getAbstractFileById(art.getObjectID()); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex); //NON-NLS return n; } - - n.addNodeProperty(new NodeProperty<>( - KeywordHits_createNodeForKey_modTime_name(), - KeywordHits_createNodeForKey_modTime_displayName(), - KeywordHits_createNodeForKey_modTime_desc(), - ContentUtils.getStringTime(file.getMtime(), file))); - n.addNodeProperty(new NodeProperty<>( - KeywordHits_createNodeForKey_accessTime_name(), - KeywordHits_createNodeForKey_accessTime_displayName(), - KeywordHits_createNodeForKey_accessTime_desc(), - ContentUtils.getStringTime(file.getAtime(), file))); - n.addNodeProperty(new NodeProperty<>( - KeywordHits_createNodeForKey_chgTime_name(), - KeywordHits_createNodeForKey_chgTime_displayName(), - KeywordHits_createNodeForKey_chgTime_desc(), - ContentUtils.getStringTime(file.getCtime(), file))); - return n; - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS } - return null; + /* + * It is possible to get a keyword hit on artifacts generated for the + * underlying image in which case MAC times are not + * available/applicable/useful. + */ + if (file == null) { + return n; + } + n.addNodeProperty(new NodeProperty<>( + KeywordHits_createNodeForKey_modTime_name(), + KeywordHits_createNodeForKey_modTime_displayName(), + KeywordHits_createNodeForKey_modTime_desc(), + ContentUtils.getStringTime(file.getMtime(), file))); + n.addNodeProperty(new NodeProperty<>( + KeywordHits_createNodeForKey_accessTime_name(), + KeywordHits_createNodeForKey_accessTime_displayName(), + KeywordHits_createNodeForKey_accessTime_desc(), + ContentUtils.getStringTime(file.getAtime(), file))); + n.addNodeProperty(new NodeProperty<>( + KeywordHits_createNodeForKey_chgTime_name(), + KeywordHits_createNodeForKey_chgTime_displayName(), + KeywordHits_createNodeForKey_chgTime_desc(), + ContentUtils.getStringTime(file.getCtime(), file))); + return n; } /** * Creates nodes for individual files that had hits */ - private class HitsFactory extends DetachableObserverChildFactory { + private class HitsFactory extends BaseChildFactory implements Observer { private final String keyword; private final String setName; private final String instance; + private final Map artifactHits = new HashMap<>(); private HitsFactory(String setName, String keyword, String instance) { - super(); + /** + * The node name passed to the parent constructor will consist of + * the set name, keyword and optionally the instance name (in the + * case of regular expression hits. This name must match the name + * set in the TermNode or RegExpInstanceNode constructors. + */ + super(setName + "_" + keyword + (DEFAULT_INSTANCE_NAME.equals(instance) ? "" : "_" + instance)); this.setName = setName; this.keyword = keyword; this.instance = instance; } @Override - protected boolean createKeys(List list) { - list.addAll(keywordResults.getArtifactIds(setName, keyword, instance)); - return true; + protected List makeKeys() { + if (skCase != null) { + keywordResults.getArtifactIds(setName, keyword, instance).forEach((id) -> { + try { + if (!artifactHits.containsKey(id)) { + BlackboardArtifact art = skCase.getBlackboardArtifact(id); + artifactHits.put(id, art); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS + } + }); + + return new ArrayList<>(artifactHits.values()); + } + return Collections.emptyList(); } @Override - protected Node createNodeForKey(Long artifactId) { - return createBlackboardArtifactNode(artifactId); + protected Node createNodeForKey(BlackboardArtifact art) { + return createBlackboardArtifactNode(art); + } + + @Override + protected void onAdd() { + keywordResults.addObserver(this); + } + + @Override + protected void onRemove() { + keywordResults.deleteObserver(this); + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); } } } From d2624731d7f365815fbea044eb3d8fe9f527ab10 Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 17 Apr 2019 14:31:30 -0400 Subject: [PATCH 07/57] Fix Codacy issues. --- .../org/sleuthkit/autopsy/datamodel/ExtractedContent.java | 8 +++----- Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index 9844811a02..a21afff190 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -501,11 +501,9 @@ public class ExtractedContent implements AutopsyVisitableItem { protected List makeKeys() { if (skCase != null) { try { - List arts = - Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ? - blackboard.getArtifacts(type.getTypeID(), datasourceObjId) : - skCase.getBlackboardArtifacts(type.getTypeID()); - return arts; + return Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) + ? blackboard.getArtifacts(type.getTypeID(), datasourceObjId) + : skCase.getBlackboardArtifacts(type.getTypeID()); } catch (TskException ex) { Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java index f970585455..9d6ed53431 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java @@ -35,7 +35,6 @@ import java.util.Observable; import java.util.Observer; import java.util.Set; import java.util.logging.Level; -import java.util.stream.Collectors; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; From 89e30cbd1382146ce1d1c0e749864532f8166768 Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 17 Apr 2019 17:56:32 -0400 Subject: [PATCH 08/57] Converted EmailExtracted to new child factory approach. --- .../autopsy/datamodel/EmailExtracted.java | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java index ff268f2de2..83fcee1b65 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012-2018 Basis Technology Corp. + * Copyright 2012-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -42,7 +42,6 @@ import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; @@ -494,41 +493,52 @@ public class EmailExtracted implements AutopsyVisitableItem { /** * Node representing mail folder content (mail messages) */ - private class MessageFactory extends ChildFactory implements Observer { + private class MessageFactory extends BaseChildFactory implements Observer { private final String accountName; private final String folderName; private MessageFactory(String accountName, String folderName) { - super(); + super(accountName + "_" + folderName); this.accountName = accountName; this.folderName = folderName; emailResults.addObserver(this); } @Override - protected boolean createKeys(List list) { - list.addAll(emailResults.getArtifactIds(accountName, folderName)); - return true; - } - - @Override - protected Node createNodeForKey(Long artifactId) { - if (skCase == null) { - return null; - } - try { - BlackboardArtifact artifact = skCase.getBlackboardArtifact(artifactId); - return new BlackboardArtifactNode(artifact); - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error creating mail messages nodes", ex); //NON-NLS - } - return null; + protected Node createNodeForKey(BlackboardArtifact art) { + return new BlackboardArtifactNode(art); } @Override public void update(Observable o, Object arg) { refresh(true); } + + @Override + protected List makeKeys() { + List keys = new ArrayList<>(); + + if (skCase != null) { + emailResults.getArtifactIds(accountName, folderName).forEach((id) -> { + try { + keys.add(skCase.getBlackboardArtifact(id)); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error getting mail messages keys", ex); //NON-NLS + } + }); + } + return keys; + } + + @Override + protected void onAdd() { + // No-op + } + + @Override + protected void onRemove() { + // No-op + } } } From 84d219295522a7a7f51709094c378a15dcc595e2 Mon Sep 17 00:00:00 2001 From: esaunders Date: Tue, 23 Apr 2019 16:47:50 -0400 Subject: [PATCH 09/57] Added paging support to DataResultViewerTable and ViewPreferences. --- .../autopsy/corecomponents/Bundle.properties | 9 + .../corecomponents/DataResultViewerTable.form | 135 ++++++- .../corecomponents/DataResultViewerTable.java | 367 +++++++++++++++++- .../corecomponents/ViewPreferencesPanel.form | 31 +- .../corecomponents/ViewPreferencesPanel.java | 34 +- .../autopsy/datamodel/BaseChildFactory.java | 81 +++- 6 files changed, 630 insertions(+), 27 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index ec177ae5e7..8ef6797f1c 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -227,3 +227,12 @@ ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title0_1=Mime type/Extensi AutopsyOptionsPanel.maxSolrMemoryLabel.text=Maximum Solr JVM Memory: AutopsyOptionsPanel.maxMemoryUnitsLabel2.text=MB AutopsyOptionsPanel.solrJVMHeapWarning.text=NOTE: Setting this too large may impact overall performance. +DataResultViewerTable.gotoPageTextField.text= +DataResultViewerTable.gotoPageLabel.AccessibleContext.accessibleName= +DataResultViewerTable.gotoPageLabel.text=Go to Page: +DataResultViewerTable.pageNextButton.text= +DataResultViewerTable.pagePrevButton.text= +DataResultViewerTable.pagesLabel.text=Pages: +DataResultViewerTable.pageNumLabel.text= +DataResultViewerTable.pageLabel.text=Page: +ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table: diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form index d6c32623a4..6b17cfd6dd 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form @@ -16,16 +16,127 @@ - + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -33,5 +144,25 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index bf18c4b831..2029bdbe5c 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012-2018 Basis Technology Corp. + * Copyright 2012-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.corecomponents; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; import java.awt.Component; import java.awt.Cursor; import java.awt.FontMetrics; @@ -26,6 +28,7 @@ import java.awt.dnd.DnDConstants; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.FeatureDescriptor; +import java.beans.PropertyChangeEvent; import java.beans.PropertyVetoException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; @@ -35,9 +38,12 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; +import java.util.prefs.PreferenceChangeEvent; import java.util.prefs.Preferences; import javax.swing.ImageIcon; +import javax.swing.JOptionPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; import static javax.swing.SwingConstants.CENTER; @@ -61,15 +67,24 @@ import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Node.Property; +import org.openide.nodes.NodeEvent; +import org.openide.nodes.NodeListener; +import org.openide.nodes.NodeMemberEvent; +import org.openide.nodes.NodeReorderEvent; import org.openide.util.ImageUtilities; import org.openide.util.NbBundle; import org.openide.util.NbPreferences; import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo; +import org.sleuthkit.autopsy.datamodel.BaseChildFactory; +import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageChangeEvent; +import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageCountChangeEvent; +import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageSizeChangeEvent; /** * A tabular result viewer that displays the children of the given root node @@ -104,6 +119,18 @@ public class DataResultViewerTable extends AbstractDataResultViewer { private final IconRendererTableListener iconRendererListener; private Node rootNode; + /** + * Multiple nodes may have been visited in the context of this + * DataResultViewerTable. We keep track of the page state for these nodes in + * the following map. + */ + private final Map nodeNameToPagingSupportMap = new ConcurrentHashMap<>(); + + /** + * The paging support instance for the current node. + */ + private PagingSupport pagingSupport = null; + /** * Constructs a tabular result viewer that displays the children of the * given root node using an OutlineView. The viewer should have an ancestor @@ -149,6 +176,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer { */ initComponents(); + initializePagingSupport(); + /* * Configure the child OutlineView (explorer view) component. */ @@ -177,6 +206,35 @@ public class DataResultViewerTable extends AbstractDataResultViewer { outline.getTableHeader().addMouseListener(outlineViewListener); } + private void initializePagingSupport() { + if (pagingSupport == null) { + pagingSupport = new PagingSupport(); + } + + // Start out with paging controls invisible + pagingSupport.togglePageControls(false); + + /** + * Set up a change listener so we know when the user changes the page + * size + */ + UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> { + switch (evt.getKey()) { + case UserPreferences.RESULTS_TABLE_PAGE_SIZE: + this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + /** + * If multiple nodes have been viewed we have to notify all + * of them about the change in page size. + */ + nodeNameToPagingSupportMap.values().forEach((ps) -> { + ps.postPageSizeChangeEvent(); + }); + break; + } + }); + } + /** * Creates a new instance of a tabular result viewer that displays the * children of a given root node using an OutlineView. This method exists to @@ -252,6 +310,55 @@ public class DataResultViewerTable extends AbstractDataResultViewer { */ if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) { this.rootNode = rootNode; + + /** + * Check to see if we have previously created a paging support + * class for this node. + */ + pagingSupport = nodeNameToPagingSupportMap.get(rootNode.getName()); + if (pagingSupport == null) { + pagingSupport = new PagingSupport(); + } + + /** + * Get the event bus to use when communicating with the child + * factory for this node and register with the bus. + */ + EventBus bus = BaseChildFactory.nodeNameToEventBusMap.get(rootNode.getName()); + pagingSupport.registerWithEventBus(bus); + + nodeNameToPagingSupportMap.put(rootNode.getName(), pagingSupport); + pagingSupport.updateControls(); + + rootNode.addNodeListener(new NodeListener() { + @Override + public void childrenAdded(NodeMemberEvent nme) { + /** + * This is the only somewhat reliable way I could find + * to reset the cursor after a page change. When you + * change page the old children nodes will be removed + * and new ones added. + */ + setCursor(null); + } + + @Override + public void childrenRemoved(NodeMemberEvent nme) { + } + + @Override + public void childrenReordered(NodeReorderEvent nre) { + } + + @Override + public void nodeDestroyed(NodeEvent ne) { + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + } + }); + this.getExplorerManager().setRootContext(this.rootNode); setupTable(); } else { @@ -665,6 +772,152 @@ public class DataResultViewerTable extends AbstractDataResultViewer { } } + /** + * Maintains the current page state for a node and provides support for + * paging through results. Uses an EventBus to communicate with child + * factory implementations. + */ + private class PagingSupport { + + private int currentPage; + private int totalPages; + private EventBus eb; + + PagingSupport() { + currentPage = 1; + totalPages = 0; + eb = null; + } + + void registerWithEventBus(EventBus bus) { + if (eb == bus) { + return; + } + + if (bus != null) { + eb = bus; + eb.register(this); + } + + updateControls(); + } + + void nextPage() { + currentPage++; + postPageChangeEvent(); + } + + void previousPage() { + currentPage--; + postPageChangeEvent(); + } + + @NbBundle.Messages({"# {0} - totalPages", + "DataResultViewerTable.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0}", + "DataResultViewerTable.goToPageTextField.err=Invalid page number"}) + void gotoPage() { + try { + currentPage = Integer.decode(gotoPageTextField.getText()); + } catch (NumberFormatException e) { + //ignore input + return; + } + + if (currentPage > totalPages || currentPage < 1) { + currentPage = 1; + JOptionPane.showMessageDialog(DataResultViewerTable.this, + Bundle.DataResultViewerTable_goToPageTextField_msgDlg(totalPages), + Bundle.DataResultViewerTable_goToPageTextField_err(), + JOptionPane.WARNING_MESSAGE); + return; + } + postPageChangeEvent(); + } + + /** + * Notify subscribers (i.e. child factories) that a page change has + * occurred. + */ + void postPageChangeEvent() { + if (eb != null) { + eb.post(new PageChangeEvent(currentPage)); + DataResultViewerTable.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + updateControls(); + } + + /** + * Notify subscribers (i.e. child factories) that a page size change has + * occurred. + */ + void postPageSizeChangeEvent() { + if (eb != null) { + // Reset page variables when page size changes + currentPage = 1; + totalPages = 0; + + if (this == pagingSupport) { + updateControls(); + } + eb.post(new PageSizeChangeEvent(UserPreferences.getResultsTablePageSize())); + } + } + + /** + * Subscribe to notification that the number of pages has changed. + * + * @param event + */ + @Subscribe + public void subscribeToPageCountChange(PageCountChangeEvent event) { + if (event != null) { + totalPages = event.getPageCount(); + if (totalPages > 1) { + // Make paging controls visible if there is more than one page. + togglePageControls(true); + } + + updateControls(); + } + } + + /** + * Make paging controls visible or invisible based on flag. + * + * @param onOff + */ + private void togglePageControls(boolean onOff) { + pageLabel.setVisible(onOff); + pagesLabel.setVisible(onOff); + pagePrevButton.setVisible(onOff); + pageNextButton.setVisible(onOff); + pageNumLabel.setVisible(onOff); + gotoPageLabel.setVisible(onOff); + gotoPageTextField.setVisible(onOff); + gotoPageTextField.setVisible(onOff); + validate(); + repaint(); + } + + private void updateControls() { + if (totalPages == 0) { + pagePrevButton.setEnabled(false); + pageNextButton.setEnabled(false); + pageNumLabel.setText(""); + gotoPageTextField.setText(""); + gotoPageTextField.setEnabled(false); + } else { + pageNumLabel.setText(NbBundle.getMessage(this.getClass(), "DataResultViewerThumbnail.pageNumbers.curOfTotal", + Integer.toString(currentPage), Integer.toString(totalPages))); + + pageNextButton.setEnabled(!(currentPage == totalPages)); + pagePrevButton.setEnabled(!(currentPage == 1)); + gotoPageTextField.setEnabled(true); + gotoPageTextField.setText(""); + } + } + } + /** * Listener which sets the custom icon renderer on columns which contain * icons instead of text when a column is added. @@ -1033,21 +1286,129 @@ public class DataResultViewerTable extends AbstractDataResultViewer { // //GEN-BEGIN:initComponents private void initComponents() { + pageLabel = new javax.swing.JLabel(); + pageNumLabel = new javax.swing.JLabel(); + pagesLabel = new javax.swing.JLabel(); + pagePrevButton = new javax.swing.JButton(); + pageNextButton = new javax.swing.JButton(); outlineView = new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL); + gotoPageLabel = new javax.swing.JLabel(); + gotoPageTextField = new javax.swing.JTextField(); + + pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageLabel.text")); // NOI18N + + pageNumLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageNumLabel.text")); // NOI18N + + pagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pagesLabel.text")); // NOI18N + + pagePrevButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N + pagePrevButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pagePrevButton.text")); // NOI18N + pagePrevButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N + pagePrevButton.setFocusable(false); + pagePrevButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + pagePrevButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); + pagePrevButton.setPreferredSize(new java.awt.Dimension(55, 23)); + pagePrevButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N + pagePrevButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + pagePrevButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pagePrevButtonActionPerformed(evt); + } + }); + + pageNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N + pageNextButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageNextButton.text")); // NOI18N + pageNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N + pageNextButton.setFocusable(false); + pageNextButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + pageNextButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); + pageNextButton.setMaximumSize(new java.awt.Dimension(27, 23)); + pageNextButton.setMinimumSize(new java.awt.Dimension(27, 23)); + pageNextButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N + pageNextButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + pageNextButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pageNextButtonActionPerformed(evt); + } + }); + + gotoPageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.gotoPageLabel.text")); // NOI18N + + gotoPageTextField.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.gotoPageTextField.text")); // NOI18N + gotoPageTextField.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + gotoPageTextFieldActionPerformed(evt); + } + }); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 691, Short.MAX_VALUE) + .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap(608, Short.MAX_VALUE) + .addComponent(pageLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(pagesLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(gotoPageLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {pageNextButton, pagePrevButton}); + layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 366, Short.MAX_VALUE) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) + .addComponent(pageLabel) + .addComponent(pageNumLabel) + .addComponent(pagesLabel) + .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(gotoPageLabel) + .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 324, Short.MAX_VALUE) + .addContainerGap()) ); + + layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {pageNextButton, pagePrevButton}); + + gotoPageLabel.getAccessibleContext().setAccessibleName(""); }// //GEN-END:initComponents + + private void pagePrevButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pagePrevButtonActionPerformed + pagingSupport.previousPage(); + }//GEN-LAST:event_pagePrevButtonActionPerformed + + private void pageNextButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pageNextButtonActionPerformed + pagingSupport.nextPage(); + }//GEN-LAST:event_pageNextButtonActionPerformed + + private void gotoPageTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_gotoPageTextFieldActionPerformed + pagingSupport.gotoPage(); + }//GEN-LAST:event_gotoPageTextFieldActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel gotoPageLabel; + private javax.swing.JTextField gotoPageTextField; private org.openide.explorer.view.OutlineView outlineView; + private javax.swing.JLabel pageLabel; + private javax.swing.JButton pageNextButton; + private javax.swing.JLabel pageNumLabel; + private javax.swing.JButton pagePrevButton; + private javax.swing.JLabel pagesLabel; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form index a348b49368..16993e0457 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form @@ -68,7 +68,7 @@ - + @@ -92,7 +92,7 @@ - + @@ -153,6 +153,11 @@ + + + + + @@ -206,6 +211,11 @@ + + + + + @@ -407,6 +417,23 @@ + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java index e65373ab76..519cb6914d 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java @@ -85,6 +85,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { TextTranslationService tts = TextTranslationService.getInstance(); fileNameTranslationColumnCheckbox.setEnabled(tts.hasProvider()); + maxResultsSpinner.setValue(UserPreferences.getResultsTablePageSize()); + // Current Case Settings boolean caseIsOpen = Case.isCaseOpen(); currentCaseSettingsPanel.setEnabled(caseIsOpen); @@ -114,6 +116,7 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { UserPreferences.setShowOnlyCurrentUserTags(hideOtherUsersTagsCheckbox.isSelected()); UserPreferences.setHideCentralRepoCommentsAndOccurrences(commentsOccurencesColumnsCheckbox.isSelected()); UserPreferences.setDisplayTranslatedFileNames(fileNameTranslationColumnCheckbox.isSelected()); + UserPreferences.setResultsTablePageSize((int)maxResultsSpinner.getValue()); storeGroupItemsInTreeByDataSource(); @@ -167,6 +170,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { translateTextLabel = new javax.swing.JLabel(); commentsOccurencesColumnWrapAroundText = new javax.swing.JLabel(); fileNameTranslationColumnCheckbox = new javax.swing.JCheckBox(); + maxResultsLabel = new javax.swing.JLabel(); + maxResultsSpinner = new javax.swing.JSpinner(); currentCaseSettingsPanel = new javax.swing.JPanel(); groupByDataSourceCheckbox = new javax.swing.JCheckBox(); currentSessionSettingsPanel = new javax.swing.JPanel(); @@ -284,6 +289,15 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { } }); + org.openide.awt.Mnemonics.setLocalizedText(maxResultsLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.maxResultsLabel.text")); // NOI18N + + maxResultsSpinner.setModel(new javax.swing.SpinnerNumberModel(0, 0, 50000, 10000)); + maxResultsSpinner.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + maxResultsSpinnerStateChanged(evt); + } + }); + javax.swing.GroupLayout globalSettingsPanelLayout = new javax.swing.GroupLayout(globalSettingsPanel); globalSettingsPanel.setLayout(globalSettingsPanelLayout); globalSettingsPanelLayout.setHorizontalGroup( @@ -333,7 +347,11 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { .addComponent(keepCurrentViewerRadioButton) .addComponent(useBestViewerRadioButton) .addComponent(useLocalTimeRadioButton) - .addComponent(useAnotherTimeRadioButton)))))) + .addComponent(useAnotherTimeRadioButton))))) + .addGroup(globalSettingsPanelLayout.createSequentialGroup() + .addComponent(maxResultsLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(maxResultsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 74, javax.swing.GroupLayout.PREFERRED_SIZE))) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); globalSettingsPanelLayout.setVerticalGroup( @@ -381,6 +399,10 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { .addComponent(translateTextLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(fileNameTranslationColumnCheckbox))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(maxResultsLabel) + .addComponent(maxResultsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); @@ -593,6 +615,14 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { } }//GEN-LAST:event_fileNameTranslationColumnCheckboxActionPerformed + private void maxResultsSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_maxResultsSpinnerStateChanged + if (immediateUpdates) { + UserPreferences.setResultsTablePageSize((int)maxResultsSpinner.getValue()); + } else { + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + } + }//GEN-LAST:event_maxResultsSpinnerStateChanged + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel centralRepoLabel; @@ -613,6 +643,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { private javax.swing.JLabel hideSlackFilesLabel; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JRadioButton keepCurrentViewerRadioButton; + private javax.swing.JLabel maxResultsLabel; + private javax.swing.JSpinner maxResultsSpinner; private javax.swing.JLabel selectFileLabel; private javax.swing.JList timeZoneList; private javax.swing.JLabel translateTextLabel; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java index 555d59b33c..68e32808a6 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; +import java.util.prefs.PreferenceChangeEvent; import java.util.stream.Collectors; import org.openide.nodes.ChildFactory; import org.sleuthkit.autopsy.core.UserPreferences; @@ -108,8 +109,8 @@ public abstract class BaseChildFactory extends ChildFactory.D } /** - * Event used to let subscribers know that the user has - * navigated to a different page. + * Event used to let subscribers know that the user has navigated to a + * different page. */ public static class PageChangeEvent { @@ -125,8 +126,7 @@ public abstract class BaseChildFactory extends ChildFactory.D } /** - * Event used to let subscribers know that the number of - * pages has changed. + * Event used to let subscribers know that the number of pages has changed. */ public static class PageCountChangeEvent { @@ -142,10 +142,19 @@ public abstract class BaseChildFactory extends ChildFactory.D } /** - * Event used to let subscribers know that paging is no - * longer required. + * Event used to let subscribers know that the page size has changed. */ - public static class PagingDestroyedEvent { + public static class PageSizeChangeEvent { + + private final int pageSize; + + public PageSizeChangeEvent(int newPageSize) { + pageSize = newPageSize; + } + + public int getPageSize() { + return pageSize; + } } /** @@ -155,7 +164,7 @@ public abstract class BaseChildFactory extends ChildFactory.D class PagingSupport { private final String nodeName; - private final int pageSize; + private int pageSize; private int currentPage; private List> pages; private EventBus bus; @@ -169,23 +178,32 @@ public abstract class BaseChildFactory extends ChildFactory.D * an EventBus. */ PagingSupport(String nodeName) { + currentPage = 1; pageSize = UserPreferences.getResultsTablePageSize(); - this.currentPage = 1; pages = new ArrayList<>(); this.nodeName = nodeName; } void initialize() { - if (pageSize > 0) { - // Only configure an EventBus if paging functionality is enabled. - if (nodeNameToEventBusMap.containsKey(nodeName)) { - bus = nodeNameToEventBusMap.get(nodeName); - } else { - bus = new EventBus(nodeName); - nodeNameToEventBusMap.put(bus.identifier(), bus); + /** + * Set up a change listener so we know when the user changes the + * page size. + */ + UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> { + switch (evt.getKey()) { + case UserPreferences.RESULTS_TABLE_PAGE_SIZE: + pageSize = UserPreferences.getResultsTablePageSize(); + break; } - bus.register(this); + }); + + if (nodeNameToEventBusMap.containsKey(nodeName)) { + bus = nodeNameToEventBusMap.get(nodeName); + } else { + bus = new EventBus(nodeName); + nodeNameToEventBusMap.put(bus.identifier(), bus); } + bus.register(this); } /** @@ -220,15 +238,40 @@ public abstract class BaseChildFactory extends ChildFactory.D } } + /** + * Receives page change events from UI components and triggers a refresh + * in the child factory. + * + * @param event + */ @Subscribe private void subscribeToPageChange(PageChangeEvent event) { - // Receives page change events from UI components and - // triggers a refresh in the child factory. if (event != null) { currentPage = event.getPageNumber(); isPageChangeEvent = true; refresh(true); } } + + /** + * Receives page size change events from UI components and triggers a + * refresh in the child factory if necessary. + * + * @param event + */ + @Subscribe + private void subscribeToPageSizeChange(PageSizeChangeEvent event) { + if (event != null) { + int newPageSize = event.getPageSize(); + if (pageSize == newPageSize) { + // No change...nothing to do. + return; + } + + pageSize = newPageSize; + currentPage = 1; + refresh(true); + } + } } } From 39a9758107ba434ee5dfe211a939437a26cbe65f Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 24 Apr 2019 12:46:00 -0400 Subject: [PATCH 10/57] Bug fix and optimization for page size change. --- .../autopsy/corecomponents/Bundle.properties | 1 + .../corecomponents/Bundle.properties-MERGED | 13 +++++++++++++ .../corecomponents/DataResultViewerTable.java | 4 +++- .../corecomponents/ViewPreferencesPanel.form | 3 +++ .../corecomponents/ViewPreferencesPanel.java | 1 + .../autopsy/datamodel/BaseChildFactory.java | 17 +++++++++++++---- 6 files changed, 34 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index 8ef6797f1c..9d44214a32 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -236,3 +236,4 @@ DataResultViewerTable.pagesLabel.text=Pages: DataResultViewerTable.pageNumLabel.text= DataResultViewerTable.pageLabel.text=Page: ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table: +ViewPreferencesPanel.maxResultsLabel.toolTipText=\nAll results are shown in the results table if this value is 0 (default).\n
You may want to consider setting this value if you have large numbers of results that are taking a long time to display.\n diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED index fba1300c93..8cd249f51f 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED @@ -33,6 +33,9 @@ DataResultViewerTable.commentRenderer.tagComment.toolTip=Comment exists on assoc DataResultViewerTable.countRender.name=O DataResultViewerTable.countRender.toolTip=O(ccurrences) indicates the number of data sources containing the item in the Central Repository DataResultViewerTable.firstColLbl=Name +DataResultViewerTable.goToPageTextField.err=Invalid page number +# {0} - totalPages +DataResultViewerTable.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0} DataResultViewerTable.scoreRender.name=S DataResultViewerTable.scoreRender.toolTip=S(core) indicates whether the item is interesting or notable DataResultViewerTable.title=Table @@ -274,3 +277,13 @@ ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title0_1=Mime type/Extensi AutopsyOptionsPanel.maxSolrMemoryLabel.text=Maximum Solr JVM Memory: AutopsyOptionsPanel.maxMemoryUnitsLabel2.text=MB AutopsyOptionsPanel.solrJVMHeapWarning.text=NOTE: Setting this too large may impact overall performance. +DataResultViewerTable.gotoPageTextField.text= +DataResultViewerTable.gotoPageLabel.AccessibleContext.accessibleName= +DataResultViewerTable.gotoPageLabel.text=Go to Page: +DataResultViewerTable.pageNextButton.text= +DataResultViewerTable.pagePrevButton.text= +DataResultViewerTable.pagesLabel.text=Pages: +DataResultViewerTable.pageNumLabel.text= +DataResultViewerTable.pageLabel.text=Page: +ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table: +ViewPreferencesPanel.maxResultsLabel.toolTipText=\nAll results are shown in the results table if this value is 0 (default).\n
You may want to consider setting this value if you have large numbers of results that are taking a long time to display.\n diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 2029bdbe5c..504623b88a 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -339,7 +339,9 @@ public class DataResultViewerTable extends AbstractDataResultViewer { * change page the old children nodes will be removed * and new ones added. */ - setCursor(null); + SwingUtilities.invokeLater(() -> { + setCursor(null); + }); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form index 16993e0457..1e3c9cbf5e 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form @@ -422,6 +422,9 @@ + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java index 519cb6914d..f1f579f982 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java @@ -290,6 +290,7 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { }); org.openide.awt.Mnemonics.setLocalizedText(maxResultsLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.maxResultsLabel.text")); // NOI18N + maxResultsLabel.setToolTipText(org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.maxResultsLabel.toolTipText")); // NOI18N maxResultsSpinner.setModel(new javax.swing.SpinnerNumberModel(0, 0, 50000, 10000)); maxResultsSpinner.addChangeListener(new javax.swing.event.ChangeListener() { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java index 68e32808a6..f389f40ecb 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java @@ -43,6 +43,7 @@ public abstract class BaseChildFactory extends ChildFactory.D private final Predicate filter; private boolean isPageChangeEvent; + private boolean isPageSizeChangeEvent; private final PagingSupport pagingSupport; @@ -56,6 +57,7 @@ public abstract class BaseChildFactory extends ChildFactory.D pagingSupport = new PagingSupport(nodeName); pagingSupport.initialize(); isPageChangeEvent = false; + isPageSizeChangeEvent = false; filter = new KnownAndSlackFilter<>(); } @@ -89,9 +91,12 @@ public abstract class BaseChildFactory extends ChildFactory.D @Override protected boolean createKeys(List toPopulate) { - // For page chage events we simply return the previously calculated - // keys, otherwise we make a new set of keys. - if (!isPageChangeEvent) { + /** + * For page change events and page size change events we simply return + * the previously calculated set of keys, otherwise we make a new set of + * keys. + */ + if (!isPageChangeEvent && !isPageSizeChangeEvent) { List allKeys = makeKeys(); // Filter keys @@ -102,8 +107,9 @@ public abstract class BaseChildFactory extends ChildFactory.D toPopulate.addAll(pagingSupport.getCurrentPage()); - // Reset page change event flag + // Reset page change and page size change event flags isPageChangeEvent = false; + isPageSizeChangeEvent = false; return true; } @@ -269,7 +275,10 @@ public abstract class BaseChildFactory extends ChildFactory.D } pageSize = newPageSize; + splitKeysIntoPages(pages.stream().flatMap(List::stream).collect(Collectors.toList())); + currentPage = 1; + isPageSizeChangeEvent = true; refresh(true); } } From 737933e36a30a11fb5cf648d945635d9049b86ba Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 24 Apr 2019 15:02:16 -0400 Subject: [PATCH 11/57] Fix for KWS node display names. --- .../autopsy/datamodel/KeywordHits.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index cc6d88da29..3cea364bf5 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -508,9 +508,11 @@ public class KeywordHits implements AutopsyVisitableItem { } private abstract class KWHitsNodeBase extends DisplayableItemNode implements Observer { - - private KWHitsNodeBase(Children children, Lookup lookup) { + private String displayName; + + private KWHitsNodeBase(Children children, Lookup lookup, String displayName) { super(children, lookup); + this.displayName = displayName; } private KWHitsNodeBase(Children children) { @@ -528,7 +530,7 @@ public class KeywordHits implements AutopsyVisitableItem { } final void updateDisplayName() { - super.setDisplayName(getDisplayName() + " (" + countTotalDescendants() + ")"); + super.setDisplayName(displayName + " (" + countTotalDescendants() + ")"); } abstract int countTotalDescendants(); @@ -543,7 +545,7 @@ public class KeywordHits implements AutopsyVisitableItem { private final String listName; private ListNode(String listName) { - super(Children.create(new TermFactory(listName), true), Lookups.singleton(listName)); + super(Children.create(new TermFactory(listName), true), Lookups.singleton(listName), listName); super.setName(listName); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS this.listName = listName; @@ -656,9 +658,8 @@ public class KeywordHits implements AutopsyVisitableItem { private final String keyword; private TermNode(String setName, String keyword) { - super(Children.create(createChildFactory(setName, keyword), true), Lookups.singleton(keyword)); + super(Children.create(createChildFactory(setName, keyword), true), Lookups.singleton(keyword), keyword); - super.setDisplayName(keyword); /** * We differentiate between the programmatic name and the display * name. The programmatic name is used to create an association with @@ -756,8 +757,8 @@ public class KeywordHits implements AutopsyVisitableItem { private final String instance; private RegExpInstanceNode(String setName, String keyword, String instance) { - super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(instance)); - super.setDisplayName(instance); //the instance represents the name of the keyword hit at this point as the keyword is the regex + super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(instance), instance); + /** * We differentiate between the programmatic name and the display * name. The programmatic name is used to create an association with From 1b0d303de899c617cf53caa3fac8e72998e041a6 Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 24 Apr 2019 16:22:54 -0400 Subject: [PATCH 12/57] Stop Codacy from whining. --- .../corecomponents/DataResultViewerTable.java | 38 ++++++++++--------- .../corecomponents/ViewPreferencesPanel.java | 1 + .../autopsy/datamodel/BaseChildFactory.java | 7 ++-- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 504623b88a..243f6e816b 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -219,18 +219,15 @@ public class DataResultViewerTable extends AbstractDataResultViewer { * size */ UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> { - switch (evt.getKey()) { - case UserPreferences.RESULTS_TABLE_PAGE_SIZE: - this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - /** - * If multiple nodes have been viewed we have to notify all - * of them about the change in page size. - */ - nodeNameToPagingSupportMap.values().forEach((ps) -> { - ps.postPageSizeChangeEvent(); - }); - break; + if (evt.getKey().equals(UserPreferences.RESULTS_TABLE_PAGE_SIZE)) { + DataResultViewerTable.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + /** + * If multiple nodes have been viewed we have to notify all of + * them about the change in page size. + */ + nodeNameToPagingSupportMap.values().forEach((ps) -> { + ps.postPageSizeChangeEvent(); + }); } }); } @@ -346,18 +343,24 @@ public class DataResultViewerTable extends AbstractDataResultViewer { @Override public void childrenRemoved(NodeMemberEvent nme) { + SwingUtilities.invokeLater(() -> { + setCursor(null); + }); } @Override public void childrenReordered(NodeReorderEvent nre) { + // No-op } @Override public void nodeDestroyed(NodeEvent ne) { + // No-op } @Override public void propertyChange(PropertyChangeEvent evt) { + // No-op } }); @@ -792,7 +795,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { } void registerWithEventBus(EventBus bus) { - if (eb == bus) { + if (eb != null && eb.equals(bus)) { return; } @@ -901,6 +904,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer { repaint(); } + @NbBundle.Messages({"# {0} - currentPage", "# {1} - totalPages", + "DataResultViewerTable.pageNumbers.curOfTotal={0} of {1}"}) private void updateControls() { if (totalPages == 0) { pagePrevButton.setEnabled(false); @@ -909,11 +914,10 @@ public class DataResultViewerTable extends AbstractDataResultViewer { gotoPageTextField.setText(""); gotoPageTextField.setEnabled(false); } else { - pageNumLabel.setText(NbBundle.getMessage(this.getClass(), "DataResultViewerThumbnail.pageNumbers.curOfTotal", - Integer.toString(currentPage), Integer.toString(totalPages))); + pageNumLabel.setText(Bundle.DataResultViewerTable_pageNumbers_curOfTotal(Integer.toString(currentPage), Integer.toString(totalPages))); - pageNextButton.setEnabled(!(currentPage == totalPages)); - pagePrevButton.setEnabled(!(currentPage == 1)); + pageNextButton.setEnabled(currentPage != totalPages); + pagePrevButton.setEnabled(currentPage != 1); gotoPageTextField.setEnabled(true); gotoPageTextField.setText(""); } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java index f1f579f982..f23caa3c61 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java @@ -35,6 +35,7 @@ import org.sleuthkit.autopsy.texttranslation.TextTranslationService; /** * Panel for configuring view preferences. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class ViewPreferencesPanel extends JPanel implements OptionsPanel { private final boolean immediateUpdates; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java index f389f40ecb..26d9ee6cc3 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java @@ -28,6 +28,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import java.util.prefs.PreferenceChangeEvent; +import java.util.prefs.PreferenceChangeListener; import java.util.stream.Collectors; import org.openide.nodes.ChildFactory; import org.sleuthkit.autopsy.core.UserPreferences; @@ -196,10 +197,8 @@ public abstract class BaseChildFactory extends ChildFactory.D * page size. */ UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> { - switch (evt.getKey()) { - case UserPreferences.RESULTS_TABLE_PAGE_SIZE: - pageSize = UserPreferences.getResultsTablePageSize(); - break; + if (evt.getKey().equals(UserPreferences.RESULTS_TABLE_PAGE_SIZE)) { + pageSize = UserPreferences.getResultsTablePageSize(); } }); From 09c3c04afb2555af7f42e608c36ca47b2dbd2d05 Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 29 Apr 2019 11:47:55 -0400 Subject: [PATCH 13/57] More Codacy fixes. --- .../sleuthkit/autopsy/corecomponents/DataResultViewerTable.java | 2 +- Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 243f6e816b..0d2c831528 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -220,7 +220,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { */ UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> { if (evt.getKey().equals(UserPreferences.RESULTS_TABLE_PAGE_SIZE)) { - DataResultViewerTable.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); /** * If multiple nodes have been viewed we have to notify all of * them about the change in page size. diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java index 26d9ee6cc3..13d7539129 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java @@ -28,7 +28,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import java.util.prefs.PreferenceChangeEvent; -import java.util.prefs.PreferenceChangeListener; import java.util.stream.Collectors; import org.openide.nodes.ChildFactory; import org.sleuthkit.autopsy.core.UserPreferences; From 8a846b493752aa50a5efc2a98d0ab8ae629efdcc Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Tue, 30 Apr 2019 12:14:47 -0400 Subject: [PATCH 14/57] Added support for extracting PDF attachments in EFE module and disabled embedded content extraction for Tika so that we do not duplicate solr text for documents supported by EFE --- ... => DocumentEmbeddedContentExtractor.java} | 49 ++++- .../EmbeddedFileExtractorIngestModule.java | 10 +- .../PDFAttachmentExtractor.java | 179 ++++++++++++++++++ .../textextractors/TikaTextExtractor.java | 6 +- 4 files changed, 231 insertions(+), 13 deletions(-) rename Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/{MSOfficeEmbeddedContentExtractor.java => DocumentEmbeddedContentExtractor.java} (93%) create mode 100755 Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/PDFAttachmentExtractor.java diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/DocumentEmbeddedContentExtractor.java similarity index 93% rename from Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java rename to Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/DocumentEmbeddedContentExtractor.java index d9c142563b..c19ef48b2f 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/DocumentEmbeddedContentExtractor.java @@ -22,8 +22,10 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -33,13 +35,11 @@ import org.apache.commons.io.IOUtils; import org.apache.poi.hwpf.usermodel.Picture; import org.apache.poi.hslf.usermodel.HSLFPictureData; import org.apache.poi.hslf.usermodel.HSLFSlideShow; -import org.apache.poi.hssf.record.RecordInputStream.LeftoverDataException; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.model.PicturesTable; import org.apache.poi.sl.usermodel.PictureData.PictureType; import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.util.RecordFormatException; import org.apache.tika.config.TikaConfig; import org.apache.tika.detect.Detector; import org.apache.tika.exception.TikaException; @@ -72,13 +72,13 @@ import org.xml.sax.SAXException; /** * Extracts embedded content (e.g. images, audio, video) from Microsoft Office - * documents (both original and OOXML forms). + * documents (both original and OOXML forms) and PDF documents. */ -class MSOfficeEmbeddedContentExtractor { +class DocumentEmbeddedContentExtractor { private final FileManager fileManager; private final IngestServices services; - private static final Logger LOGGER = Logger.getLogger(MSOfficeEmbeddedContentExtractor.class.getName()); + private static final Logger LOGGER = Logger.getLogger(EmbeddedDocumentExtractor.class.getName()); private final IngestJobContext context; private String parentFileName; private final String UNKNOWN_IMAGE_NAME_PREFIX = "image_"; //NON-NLS @@ -101,7 +101,8 @@ class MSOfficeEmbeddedContentExtractor { PPT("application/vnd.ms-powerpoint"), //NON-NLS PPTX("application/vnd.openxmlformats-officedocument.presentationml.presentation"), //NON-NLS XLS("application/vnd.ms-excel"), //NON-NLS - XLSX("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); //NON-NLS + XLSX("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"), //NON-NLS + PDF("application/pdf"); //NON-NLS private final String mimeType; @@ -116,7 +117,7 @@ class MSOfficeEmbeddedContentExtractor { } private SupportedExtractionFormats abstractFileExtractionFormat; - MSOfficeEmbeddedContentExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) throws NoCurrentCaseException { + DocumentEmbeddedContentExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) throws NoCurrentCaseException { this.fileManager = Case.getCurrentCaseThrows().getServices().getFileManager(); this.services = IngestServices.getInstance(); @@ -190,6 +191,9 @@ class MSOfficeEmbeddedContentExtractor { case XLS: listOfExtractedImages = extractImagesFromXls(abstractFile); break; + case PDF: + listOfExtractedImages = extractEmbeddedContentFromPDF(abstractFile); + break; default: break; } @@ -470,6 +474,37 @@ class MSOfficeEmbeddedContentExtractor { return listOfExtractedImages; } + + /** + * + * @param abstractFile + * @return + */ + private List extractEmbeddedContentFromPDF(AbstractFile abstractFile) { + PDFAttachmentExtractor pdfExtractor = new PDFAttachmentExtractor(parser); + try { + Path outputDirectory = Paths.get(getOutputFolderPath(parentFileName)); + //Get map of attachment name -> location disk. + Map extractedAttachments = pdfExtractor.extract( + new ReadContentInputStream(abstractFile), abstractFile.getId(), + outputDirectory); + + //Convert output to hook into the existing logic for creating derived files + List extractedFiles = new ArrayList<>(); + extractedAttachments.entrySet().forEach((pathEntry) -> { + String fileName = pathEntry.getKey(); + Path writeLocation = pathEntry.getValue(); + extractedFiles.add(new ExtractedFile(fileName, + getFileRelativePath(writeLocation.getFileName().toString()), + writeLocation.toFile().length())); + }); + + return extractedFiles; + } catch (IOException | SAXException | TikaException ex) { + LOGGER.log(Level.WARNING, "Error attempting to extract attachments from PDFs", ex); //NON-NLS + } + return Collections.emptyList(); + } /** * Writes image to the module output location. diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java index 66c7f7030d..fd833b59a7 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java @@ -50,7 +50,7 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda //Outer concurrent hashmap with keys of JobID, inner concurrentHashmap with keys of objectID private static final ConcurrentHashMap> mapOfDepthTrees = new ConcurrentHashMap<>(); private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter(); - private MSOfficeEmbeddedContentExtractor officeExtractor; + private DocumentEmbeddedContentExtractor documentExtractor; private SevenZipExtractor archiveExtractor; private FileTypeDetector fileTypeDetector; private long jobId; @@ -115,10 +115,10 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda } /* * Construct an embedded content extractor for processing Microsoft - * Office documents. + * Office documents and PDF documents. */ try { - this.officeExtractor = new MSOfficeEmbeddedContentExtractor(context, fileTypeDetector, moduleDirRelative, moduleDirAbsolute); + this.documentExtractor = new DocumentEmbeddedContentExtractor(context, fileTypeDetector, moduleDirRelative, moduleDirAbsolute); } catch (NoCurrentCaseException ex) { throw new IngestModuleException(Bundle.EmbeddedFileExtractorIngestModule_UnableToGetMSOfficeExtractor_errMsg(), ex); } @@ -155,8 +155,8 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda */ if (archiveExtractor.isSevenZipExtractionSupported(abstractFile)) { archiveExtractor.unpack(abstractFile, mapOfDepthTrees.get(jobId)); - } else if (officeExtractor.isContentExtractionSupported(abstractFile)) { - officeExtractor.extractEmbeddedContent(abstractFile); + } else if (documentExtractor.isContentExtractionSupported(abstractFile)) { + documentExtractor.extractEmbeddedContent(abstractFile); } return ProcessResult.OK; } diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/PDFAttachmentExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/PDFAttachmentExtractor.java new file mode 100755 index 0000000000..ae3d967ef3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/PDFAttachmentExtractor.java @@ -0,0 +1,179 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit 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.modules.embeddedfileextractor; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; +import org.apache.tika.exception.TikaException; +import org.apache.tika.extractor.EmbeddedDocumentExtractor; +import org.apache.tika.metadata.Metadata; +import org.apache.tika.parser.AutoDetectParser; +import org.apache.tika.parser.ParseContext; +import org.apache.tika.parser.Parser; +import org.apache.tika.sax.BodyContentHandler; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.EncodedFileOutputStream; +import org.sleuthkit.datamodel.TskData; + +/** + * Facility for extracting and storing attachments from PDF documents. + * Implementation specifics, however, are generic enough to be used on any + * document with embedded resources. The current name reflects the only known + * use case for this class. + */ +final class PDFAttachmentExtractor { + + private static Logger logger = Logger.getLogger(PDFAttachmentExtractor.class.getName()); + private final AutoDetectParser parser; + + public PDFAttachmentExtractor() { + parser = new AutoDetectParser(); + } + + public PDFAttachmentExtractor(AutoDetectParser parser) { + this.parser = parser; + } + + /** + * The public endpoint + * + * @param input + * @param parentID + * @param outputDir + * @return + * @throws IOException + * @throws SAXException + * @throws TikaException + */ + public Map extract(InputStream input, long parentID, Path outputDir) throws IOException, SAXException, TikaException { + ExtractionPreconditions.checkArgument(Files.exists(outputDir), + String.format("Output directory: %s, does not exist.", outputDir.toString())); //NON-NLS + + ParseContext parseContext = new ParseContext(); + parseContext.set(Parser.class, parser); + + //Keep track of the attachment files as they are being extracted and written to disk. + NewResourceWatcher watcher = new NewResourceWatcher(); + parseContext.set(EmbeddedDocumentExtractor.class, new EmbeddedAttachmentHandler(outputDir, parentID, watcher)); + + //Parse input with default params, except for our ParseContext + parser.parse(input, new BodyContentHandler(-1), new Metadata(), parseContext); + + return watcher.getSnapshot(); + } + + /** + * Internal Tika class that is invoked upon encountering an embedded + * resource. + */ + static class EmbeddedAttachmentHandler implements EmbeddedDocumentExtractor { + + private final Path outputDirectory; + private final NewResourceWatcher watcher; + private final Long parentID; + private Integer attachmentCount; + + public EmbeddedAttachmentHandler(Path outputDirectory, long parentID, NewResourceWatcher watcher) { + this.outputDirectory = outputDirectory; + this.watcher = watcher; + this.parentID = parentID; + attachmentCount = 0; + } + + @Override + public boolean shouldParseEmbedded(Metadata mtdt) { + //Grab every available attachment + return true; + } + + @Override + public void parseEmbedded(InputStream in, ContentHandler ch, Metadata mtdt, boolean bln) throws SAXException, IOException { + //Resource naming scheme is used internally in autopsy, therefore we can guarentee uniqueness. + String uniqueExtractedName = parentID + "_attch_" + attachmentCount++; //NON-NLS + + String name = mtdt.get(Metadata.RESOURCE_NAME_KEY); + String ext = FilenameUtils.getExtension(name); + + //Append the extension if we can. + if(ext == null) { + name = uniqueExtractedName; + } else if(!ext.isEmpty()) { + uniqueExtractedName += "." + ext; + } + + Path outputFile = outputDirectory.resolve(uniqueExtractedName); + + try (EncodedFileOutputStream outputStream = new EncodedFileOutputStream( + new FileOutputStream(outputFile.toFile()), TskData.EncodingType.XOR1)){ + IOUtils.copy(in, outputStream); + watcher.notify(name, outputFile); + } catch (IOException ex) { + logger.log(Level.WARNING, String.format("Could not extract attachment %s into directory %s", //NON-NLS + uniqueExtractedName, outputFile), ex); + } + } + } + + /** + * Convenient wrapper for keeping track of new resource paths and the display + * name for each of these resources. + * + * It is necessary to maintain a snapshot of only our changes when the + * output directory is shared among other processes/threads. + */ + static class NewResourceWatcher { + + private final Map newResourcePaths; + + public NewResourceWatcher() { + newResourcePaths = new HashMap<>(); + } + + public void notify(String name, Path newResource) { + newResourcePaths.put(name, newResource); + } + + public Map getSnapshot() { + return newResourcePaths; + } + } + + /** + * Static convenience methods that ensure the PDF extractor is being invoked + * correctly. + */ + static class ExtractionPreconditions { + + public static void checkArgument(boolean expression, String msg) throws IOException { + if (!expression) { + throw new IOException(msg); + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/TikaTextExtractor.java b/Core/src/org/sleuthkit/autopsy/textextractors/TikaTextExtractor.java index b4a53b2e55..b7cab58101 100644 --- a/Core/src/org/sleuthkit/autopsy/textextractors/TikaTextExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/textextractors/TikaTextExtractor.java @@ -44,6 +44,7 @@ import java.util.stream.Stream; import org.apache.tika.Tika; import org.apache.tika.metadata.Metadata; import org.apache.tika.parser.AutoDetectParser; +import org.apache.tika.parser.EmptyParser; import org.apache.tika.parser.ParseContext; import org.apache.tika.parser.Parser; import org.apache.tika.parser.ParsingReader; @@ -177,7 +178,10 @@ final class TikaTextExtractor implements TextExtractor { InputStream stream = null; ParseContext parseContext = new ParseContext(); - parseContext.set(Parser.class, parser); + + //Disable appending embedded file text to output + //JIRA-4975 + parseContext.set(Parser.class, new EmptyParser()); if (ocrEnabled() && content instanceof AbstractFile) { AbstractFile file = ((AbstractFile) content); From 35e3934816256f20f569c5ada6cf738ef19d1c86 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Tue, 30 Apr 2019 12:28:04 -0400 Subject: [PATCH 15/57] Only turn off embedded extraction for known mime-types --- .../textextractors/TikaTextExtractor.java | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/TikaTextExtractor.java b/Core/src/org/sleuthkit/autopsy/textextractors/TikaTextExtractor.java index b7cab58101..fa1deaa9aa 100644 --- a/Core/src/org/sleuthkit/autopsy/textextractors/TikaTextExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/textextractors/TikaTextExtractor.java @@ -120,6 +120,16 @@ final class TikaTextExtractor implements TextExtractor { "application/x-z", //NON-NLS "application/x-compress"); //NON-NLS + //Tika should ignore types with embedded files that can be handled by the unpacking modules + private static final List EMBEDDED_FILE_MIME_TYPES + = ImmutableList.of("application/msword", //NON-NLS + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", //NON-NLS + "application/vnd.ms-powerpoint", //NON-NLS + "application/vnd.openxmlformats-officedocument.presentationml.presentation", //NON-NLS + "application/vnd.ms-excel", //NON-NLS + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", //NON-NLS + "application/pdf"); //NON-NLS + private static final java.util.logging.Logger TIKA_LOGGER = java.util.logging.Logger.getLogger("Tika"); //NON-NLS private static final Logger AUTOPSY_LOGGER = Logger.getLogger(TikaTextExtractor.class.getName()); @@ -137,7 +147,7 @@ final class TikaTextExtractor implements TextExtractor { private static final File TESSERACT_PATH = locateTesseractExecutable(); private String languagePacks = formatLanguagePacks(PlatformUtil.getOcrLanguagePacks()); private static final String TESSERACT_OUTPUT_FILE_NAME = "tess_output"; //NON-NLS - + private ProcessTerminator processTerminator; private static final List TIKA_SUPPORTED_TYPES @@ -152,8 +162,8 @@ final class TikaTextExtractor implements TextExtractor { /** * If Tesseract has been installed and is set to be used through - * configuration, then ocr is enabled. OCR can only currently be run on - * 64 bit Windows OS. + * configuration, then ocr is enabled. OCR can only currently be run on 64 + * bit Windows OS. * * @return Flag indicating if OCR is set to be used. */ @@ -178,10 +188,14 @@ final class TikaTextExtractor implements TextExtractor { InputStream stream = null; ParseContext parseContext = new ParseContext(); - - //Disable appending embedded file text to output + + //Disable appending embedded file text to output for EFE supported types //JIRA-4975 - parseContext.set(Parser.class, new EmptyParser()); + if(content instanceof AbstractFile && EMBEDDED_FILE_MIME_TYPES.contains(((AbstractFile)content).getMIMEType())) { + parseContext.set(Parser.class, new EmptyParser()); + } else { + parseContext.set(Parser.class, parser); + } if (ocrEnabled() && content instanceof AbstractFile) { AbstractFile file = ((AbstractFile) content); @@ -205,7 +219,7 @@ final class TikaTextExtractor implements TextExtractor { TesseractOCRConfig ocrConfig = new TesseractOCRConfig(); String tesseractFolder = TESSERACT_PATH.getParent(); ocrConfig.setTesseractPath(tesseractFolder); - + ocrConfig.setLanguage(languagePacks); ocrConfig.setTessdataPath(PlatformUtil.getOcrLanguagePacksPath()); parseContext.set(TesseractOCRConfig.class, ocrConfig); @@ -277,7 +291,7 @@ final class TikaTextExtractor implements TextExtractor { File outputFile = null; try { String tempDirectory = Case.getCurrentCaseThrows().getTempDirectory(); - + //Appending file id makes the name unique String tempFileName = FileUtil.escapeFileName(file.getId() + file.getName()); inputFile = Paths.get(tempDirectory, tempFileName).toFile(); @@ -318,7 +332,7 @@ final class TikaTextExtractor implements TextExtractor { } } } - + /** * Wraps the creation of a TikaReader into a Future so that it can be * cancelled. @@ -430,11 +444,11 @@ final class TikaTextExtractor implements TextExtractor { */ @Override public boolean isSupported() { - if(!(content instanceof AbstractFile)) { + if (!(content instanceof AbstractFile)) { return false; } - - String detectedType = ((AbstractFile)content).getMIMEType(); + + String detectedType = ((AbstractFile) content).getMIMEType(); if (detectedType == null || BINARY_MIME_TYPES.contains(detectedType) //any binary unstructured blobs (string extraction will be used) || ARCHIVE_MIME_TYPES.contains(detectedType) @@ -443,7 +457,7 @@ final class TikaTextExtractor implements TextExtractor { ) { return false; } - + return TIKA_SUPPORTED_TYPES.contains(detectedType); } @@ -493,11 +507,11 @@ final class TikaTextExtractor implements TextExtractor { if (context != null) { ImageConfig configInstance = context.lookup(ImageConfig.class); if (configInstance != null) { - if(Objects.nonNull(configInstance.getOCREnabled())) { + if (Objects.nonNull(configInstance.getOCREnabled())) { this.tesseractOCREnabled = configInstance.getOCREnabled(); } - - if(Objects.nonNull(configInstance.getOCRLanguages())) { + + if (Objects.nonNull(configInstance.getOCRLanguages())) { this.languagePacks = formatLanguagePacks(configInstance.getOCRLanguages()); } } From 68950b53722a8951155f661cd65abb2da2b096cd Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Tue, 30 Apr 2019 12:39:39 -0400 Subject: [PATCH 16/57] Added a comment and fixed a typo --- .../DocumentEmbeddedContentExtractor.java | 2 +- .../PDFAttachmentExtractor.java | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/DocumentEmbeddedContentExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/DocumentEmbeddedContentExtractor.java index c19ef48b2f..e6a0810e78 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/DocumentEmbeddedContentExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/DocumentEmbeddedContentExtractor.java @@ -78,7 +78,7 @@ class DocumentEmbeddedContentExtractor { private final FileManager fileManager; private final IngestServices services; - private static final Logger LOGGER = Logger.getLogger(EmbeddedDocumentExtractor.class.getName()); + private static final Logger LOGGER = Logger.getLogger(DocumentEmbeddedContentExtractor.class.getName()); private final IngestJobContext context; private String parentFileName; private final String UNKNOWN_IMAGE_NAME_PREFIX = "image_"; //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/PDFAttachmentExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/PDFAttachmentExtractor.java index ae3d967ef3..7a0747b648 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/PDFAttachmentExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/PDFAttachmentExtractor.java @@ -61,15 +61,16 @@ final class PDFAttachmentExtractor { } /** - * The public endpoint + * Extracts PDF attachments from a given input and writes them to the supplied + * output directory. * - * @param input - * @param parentID - * @param outputDir - * @return + * @param input Input PDF to extract attachments from + * @param parentID ID for unique extraction names + * @param outputDir Directory to write attachments + * @return Map containing file name -> location on disk * @throws IOException * @throws SAXException - * @throws TikaException + * @throws TikaException */ public Map extract(InputStream input, long parentID, Path outputDir) throws IOException, SAXException, TikaException { ExtractionPreconditions.checkArgument(Files.exists(outputDir), From a09d52dafc0a6242ec73f146cd5c96a9c61b2444 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Tue, 30 Apr 2019 12:41:39 -0400 Subject: [PATCH 17/57] One last comment --- .../DocumentEmbeddedContentExtractor.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/DocumentEmbeddedContentExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/DocumentEmbeddedContentExtractor.java index e6a0810e78..a362c5789f 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/DocumentEmbeddedContentExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/DocumentEmbeddedContentExtractor.java @@ -476,9 +476,10 @@ class DocumentEmbeddedContentExtractor { } /** + * Extracts embedded attachments from PDF files. * - * @param abstractFile - * @return + * @param abstractFile Input PDF file + * @return List of extracted files to be made into derived file instances. */ private List extractEmbeddedContentFromPDF(AbstractFile abstractFile) { PDFAttachmentExtractor pdfExtractor = new PDFAttachmentExtractor(parser); From 8594378a38ae6d5d42d16ffeb55f7557397b889e Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 1 May 2019 17:36:44 -0400 Subject: [PATCH 18/57] Initial work on adding paging to data sources tree. --- .../datamodel/AbstractAbstractFileNode.java | 10 +- .../datamodel/AbstractContentChildren.java | 216 +----------------- .../datamodel/AbstractContentNode.java | 5 +- .../datamodel/AutopsyTreeChildFactory.java | 2 +- .../autopsy/datamodel/BaseChildFactory.java | 5 +- .../autopsy/datamodel/ContentChildren.java | 38 ++- .../datamodel/CreateSleuthkitNodeVisitor.java | 102 +++++++++ .../autopsy/datamodel/DataSourcesNode.java | 29 +-- .../autopsy/datamodel/ImageNode.java | 2 +- .../datamodel/RootContentChildren.java | 126 +++++++++- .../autopsy/datamodel/VolumeNode.java | 2 +- .../directorytree/DataResultFilterNode.java | 22 +- .../ViewAssociatedContentAction.java | 8 +- 13 files changed, 285 insertions(+), 282 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/CreateSleuthkitNodeVisitor.java diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index 05f80dc5d3..0cafd68bc7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -169,10 +169,12 @@ public abstract class AbstractAbstractFileNode extends A // data sources branch of the tree. The parent nodes in other // branches of the tree (e.g. File Types and Deleted Files) do // not need to be refreshed. - if (parentsChildren instanceof ContentChildren) { - ((ContentChildren) parentsChildren).refreshChildren(); - parentsChildren.getNodesCount(); - } + +// TODO: How will this work with ChildFactory approach? +// if (parentsChildren instanceof ContentChildren) { +// ((ContentChildren) parentsChildren).refreshChildren(); +// parentsChildren.getNodesCount(); +// } } catch (NullPointerException ex) { // Skip } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java index 71705d2725..1a01cc0a40 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,228 +18,28 @@ */ package org.sleuthkit.autopsy.datamodel; -import org.openide.nodes.AbstractNode; -import org.openide.nodes.Children.Keys; import org.openide.nodes.Node; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesNode; -import org.sleuthkit.autopsy.datamodel.accounts.Accounts; -import org.sleuthkit.autopsy.datamodel.accounts.Accounts.AccountsRootNode; -import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.DerivedFile; -import org.sleuthkit.datamodel.Directory; -import org.sleuthkit.datamodel.File; -import org.sleuthkit.datamodel.Image; -import org.sleuthkit.datamodel.LayoutFile; -import org.sleuthkit.datamodel.LocalFile; -import org.sleuthkit.datamodel.LocalDirectory; -import org.sleuthkit.datamodel.SlackFile; -import org.sleuthkit.datamodel.SleuthkitItemVisitor; import org.sleuthkit.datamodel.SleuthkitVisitableItem; -import org.sleuthkit.datamodel.VirtualDirectory; -import org.sleuthkit.datamodel.Volume; /** - * Abstract subclass for ContentChildren and RootContentChildren implementations + * Abstract subclass for ContentChildren implementation * that handles creating Nodes from Content objects. */ -abstract class AbstractContentChildren extends Keys { +abstract class AbstractContentChildren extends BaseChildFactory { private final CreateSleuthkitNodeVisitor createSleuthkitNodeVisitor = new CreateSleuthkitNodeVisitor(); - private final CreateAutopsyNodeVisitor createAutopsyNodeVisitor = new CreateAutopsyNodeVisitor(); - /** - * Uses lazy Content.Keys - */ - AbstractContentChildren() { - /* - * This was turned off because we were getting out of memory errors when - * the filter nodes were hiding nodes. Turning this off seemed to help - */ - super(false); //don't use lazy behavior + AbstractContentChildren(String nodeName) { + super(nodeName); } @Override - protected Node[] createNodes(T key) { + protected Node createNodeForKey(T key) { if (key instanceof SleuthkitVisitableItem) { - return new Node[]{((SleuthkitVisitableItem) key).accept(createSleuthkitNodeVisitor)}; + return ((SleuthkitVisitableItem) key).accept(createSleuthkitNodeVisitor); } else { - return new Node[]{((AutopsyVisitableItem) key).accept(createAutopsyNodeVisitor)}; - } - } - - /** - * Creates appropriate Node for each sub-class of Content - */ - public static class CreateSleuthkitNodeVisitor extends SleuthkitItemVisitor.Default> { - - @Override - public AbstractContentNode visit(Directory drctr) { - return new DirectoryNode(drctr); - } - - @Override - public AbstractContentNode visit(File file) { - return new FileNode(file); - } - - @Override - public AbstractContentNode visit(Image image) { - return new ImageNode(image); - } - - @Override - public AbstractContentNode visit(Volume volume) { - return new VolumeNode(volume); - } - - @Override - public AbstractContentNode visit(LayoutFile lf) { - return new LayoutFileNode(lf); - } - - @Override - public AbstractContentNode visit(DerivedFile df) { - return new LocalFileNode(df); - } - - @Override - public AbstractContentNode visit(LocalFile lf) { - return new LocalFileNode(lf); - } - - @Override - public AbstractContentNode visit(VirtualDirectory ld) { - return new VirtualDirectoryNode(ld); - } - - @Override - public AbstractContentNode visit(LocalDirectory ld) { - return new LocalDirectoryNode(ld); - } - - @Override - public AbstractContentNode visit(SlackFile sf) { - return new SlackFileNode(sf); - } - - @Override - public AbstractContentNode visit(BlackboardArtifact art) { - return new BlackboardArtifactNode(art); - } - - @Override - protected AbstractContentNode defaultVisit(SleuthkitVisitableItem di) { - throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), - "AbstractContentChildren.CreateTSKNodeVisitor.exception.noNodeMsg")); - } - } - - /** - * Gets a DisplayableItemNode for use as a subtree root node for the Autopsy - * tree view from each type of AutopsyVisitableItem visited. There are - * AutopsyVisitableItems for the Data Sources, Views, Results, and Reports - * subtrees, and for the subtrees of Results (e.g., Extracted Content, Hash - * Set Hits, etc.). - */ - static class CreateAutopsyNodeVisitor extends AutopsyItemVisitor.Default { - - @Override - public ExtractedContent.RootNode visit(ExtractedContent ec) { - return ec.new RootNode(ec.getSleuthkitCase()); - } - - @Override - public AbstractNode visit(FileTypesByExtension sf) { - return sf.new FileTypesByExtNode(sf.getSleuthkitCase(), null); - } - - @Override - public AbstractNode visit(RecentFiles rf) { - return new RecentFilesNode(rf.getSleuthkitCase()); - } - - @Override - public AbstractNode visit(DeletedContent dc) { - return new DeletedContent.DeletedContentsNode(dc.getSleuthkitCase(), dc.filteringDataSourceObjId()); - } - - @Override - public AbstractNode visit(FileSize dc) { - return new FileSize.FileSizeRootNode(dc.getSleuthkitCase(), dc.filteringDataSourceObjId()); - } - - @Override - public AbstractNode visit(KeywordHits kh) { - return kh.new RootNode(); - } - - @Override - public AbstractNode visit(HashsetHits hh) { - return hh.new RootNode(); - } - - @Override - public AbstractNode visit(InterestingHits ih) { - return ih.new RootNode(); - } - - @Override - public AbstractNode visit(EmailExtracted ee) { - return ee.new RootNode(); - } - - @Override - public AbstractNode visit(Tags tagsNodeKey) { - return tagsNodeKey.new RootNode(tagsNodeKey.filteringDataSourceObjId()); - } - - @Override - public AbstractNode visit(DataSources i) { - return new DataSourcesNode(i.filteringDataSourceObjId()); - } - - @Override - public AbstractNode visit(DataSourceGrouping datasourceGrouping) { - return new DataSourceGroupingNode(datasourceGrouping.getDataSource()); - } - - @Override - public AbstractNode visit(Views v) { - return new ViewsNode(v.getSleuthkitCase(), v.filteringDataSourceObjId()); - } - - @Override - public AbstractNode visit(Results results) { - return new ResultsNode(results.getSleuthkitCase(), results.filteringDataSourceObjId() ); - } - - @Override - public AbstractNode visit(FileTypes ft) { - return ft.new FileTypesNode(); - } - - @Override - public AbstractNode visit(Reports reportsItem) { - return new Reports.ReportsListNode(); - } - - @Override - public AbstractNode visit(Accounts accountsItem) { - return accountsItem.new AccountsRootNode(); - } - - @Override - protected AbstractNode defaultVisit(AutopsyVisitableItem di) { - throw new UnsupportedOperationException( - NbBundle.getMessage(this.getClass(), - "AbstractContentChildren.createAutopsyNodeVisitor.exception.noNodeMsg")); - } - - @Override - public AbstractNode visit(FileTypesByMimeType ftByMimeTypeItem) { - return ftByMimeTypeItem.new ByMimeTypeNode(); + return null; } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index 3fc3fa9f4e..4f2010fd94 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +22,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.logging.Level; +import org.openide.nodes.Children; import org.openide.util.lookup.Lookups; import org.openide.util.Lookup; @@ -66,7 +67,7 @@ public abstract class AbstractContentNode extends ContentNode */ AbstractContentNode(T content, Lookup lookup) { //TODO consider child factory for the content children - super(new ContentChildren(content), lookup); + super(Children.create(new ContentChildren(content), true), lookup); this.content = content; //super.setName(ContentUtils.getSystemName(content)); super.setName("content_" + Long.toString(content.getId())); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java index 69e3787e99..b8625b782e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java @@ -135,7 +135,7 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable extends ChildFactory.D if (!isPageChangeEvent && !isPageSizeChangeEvent) { List allKeys = makeKeys(); - // Filter keys - allKeys.stream().filter(filter).collect(Collectors.toList()); - - pagingSupport.splitKeysIntoPages(allKeys); + pagingSupport.splitKeysIntoPages(allKeys.stream().filter(filter).collect(Collectors.toList())); } toPopulate.addAll(pagingSupport.getCurrentPage()); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/ContentChildren.java index cc03bb4d46..44885e1ef8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ContentChildren.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,17 +33,15 @@ import org.sleuthkit.datamodel.VolumeSystem; /** * Makes the children nodes / keys for a given content object. Has knowledge * about the structure of the directory tree and what levels should be ignored. - * TODO consider a ContentChildren child factory */ class ContentChildren extends AbstractContentChildren { private static final Logger logger = Logger.getLogger(ContentChildren.class.getName()); - //private static final int MAX_CHILD_COUNT = 1000000; private final Content parent; ContentChildren(Content parent) { - super(); //initialize lazy behavior + super("content_" + Long.toString(parent.getId())); this.parent = parent; } @@ -90,7 +88,7 @@ class ContentChildren extends AbstractContentChildren { children.add(c); } } else if (c instanceof LocalDirectory) { - LocalDirectory localDir = (LocalDirectory)c; + LocalDirectory localDir = (LocalDirectory) c; if (localDir.isRoot()) { children.addAll(getDisplayChildren(localDir)); } else { @@ -104,27 +102,13 @@ class ContentChildren extends AbstractContentChildren { } @Override - protected void addNotify() { - super.addNotify(); - - //TODO check global settings - //if above limit, query and return subrange - //StopWatch s2 = new StopWatch(); - //s2.start(); - //logger.log(Level.INFO, "GETTING CHILDREN CONTENT for parent: " + parent.getName()); - List children = getDisplayChildren(parent); - //s2.stop(); - //logger.log(Level.INFO, "GOT CHILDREN CONTENTS:" + children.size() + ", took: " + s2.getElapsedTime()); - - //limit number children - //setKeys(children.subList(0, Math.min(children.size(), MAX_CHILD_COUNT))); - setKeys(children); + protected List makeKeys() { + return getDisplayChildren(parent); } @Override - protected void removeNotify() { - super.removeNotify(); - setKeys(new ArrayList<>()); + protected void onAdd() { + // No-op } /** @@ -133,7 +117,11 @@ class ContentChildren extends AbstractContentChildren { * them). */ void refreshChildren() { - List children = getDisplayChildren(parent); - setKeys(children); + refresh(true); + } + + @Override + protected void onRemove() { + // No-op } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/CreateSleuthkitNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/CreateSleuthkitNodeVisitor.java new file mode 100644 index 0000000000..d9ff5fc33b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/CreateSleuthkitNodeVisitor.java @@ -0,0 +1,102 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2019 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datamodel; + +import org.openide.util.NbBundle; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.DerivedFile; +import org.sleuthkit.datamodel.Directory; +import org.sleuthkit.datamodel.File; +import org.sleuthkit.datamodel.Image; +import org.sleuthkit.datamodel.LayoutFile; +import org.sleuthkit.datamodel.LocalDirectory; +import org.sleuthkit.datamodel.LocalFile; +import org.sleuthkit.datamodel.SlackFile; +import org.sleuthkit.datamodel.SleuthkitItemVisitor; +import org.sleuthkit.datamodel.SleuthkitVisitableItem; +import org.sleuthkit.datamodel.VirtualDirectory; +import org.sleuthkit.datamodel.Volume; + +/** + * Creates appropriate Node for each sub-class of Content + */ +public class CreateSleuthkitNodeVisitor extends SleuthkitItemVisitor.Default> { + + @Override + public AbstractContentNode visit(Directory drctr) { + return new DirectoryNode(drctr); + } + + @Override + public AbstractContentNode visit(File file) { + return new FileNode(file); + } + + @Override + public AbstractContentNode visit(Image image) { + return new ImageNode(image); + } + + @Override + public AbstractContentNode visit(Volume volume) { + return new VolumeNode(volume); + } + + @Override + public AbstractContentNode visit(LayoutFile lf) { + return new LayoutFileNode(lf); + } + + @Override + public AbstractContentNode visit(DerivedFile df) { + return new LocalFileNode(df); + } + + @Override + public AbstractContentNode visit(LocalFile lf) { + return new LocalFileNode(lf); + } + + @Override + public AbstractContentNode visit(VirtualDirectory ld) { + return new VirtualDirectoryNode(ld); + } + + @Override + public AbstractContentNode visit(LocalDirectory ld) { + return new LocalDirectoryNode(ld); + } + + @Override + public AbstractContentNode visit(SlackFile sf) { + return new SlackFileNode(sf); + } + + @Override + public AbstractContentNode visit(BlackboardArtifact art) { + return new BlackboardArtifactNode(art); + } + + @Override + protected AbstractContentNode defaultVisit(SleuthkitVisitableItem di) { + throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), + "AbstractContentChildren.CreateTSKNodeVisitor.exception.noNodeMsg")); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java index 5b3f2fa32a..1265db5658 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java @@ -27,6 +27,7 @@ import java.util.Comparator; import java.util.EnumSet; import java.util.List; import java.util.logging.Level; +import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; @@ -56,7 +57,7 @@ public class DataSourcesNode extends DisplayableItemNode { } public DataSourcesNode(long dsObjId) { - super(new DataSourcesNodeChildren(dsObjId), Lookups.singleton(NAME)); + super(Children.create(new DataSourcesNodeChildren(dsObjId), true), Lookups.singleton(NAME)); displayName = (dsObjId > 0) ? NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.group_by_datasource.name") : NAME; init(); } @@ -87,7 +88,7 @@ public class DataSourcesNode extends DisplayableItemNode { } public DataSourcesNodeChildren(long dsObjId) { - super(); + super("ds_" + Long.toString(dsObjId)); this.currentKeys = new ArrayList<>(); this.datasourceObjId = dsObjId; } @@ -97,25 +98,24 @@ public class DataSourcesNode extends DisplayableItemNode { public void propertyChange(PropertyChangeEvent evt) { String eventType = evt.getPropertyName(); if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { - reloadKeys(); + refresh(true); } } }; @Override - protected void addNotify() { + protected void onAdd() { Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl); - reloadKeys(); } @Override - protected void removeNotify() { + protected void onRemove() { Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl); currentKeys.clear(); - setKeys(Collections.emptySet()); } - private void reloadKeys() { + @Override + protected List makeKeys() { try { if (datasourceObjId == 0) { currentKeys = Case.getCurrentCaseThrows().getDataSources(); @@ -135,20 +135,11 @@ public class DataSourcesNode extends DisplayableItemNode { }); - setKeys(currentKeys); } catch (TskCoreException | NoCurrentCaseException | TskDataException ex) { logger.log(Level.SEVERE, "Error getting data sources: {0}", ex.getMessage()); // NON-NLS - setKeys(Collections.emptySet()); - } - } - - /** - * Refresh all content keys This creates new nodes of keys have changed. - */ - public void refreshContentKeys() { - for (Content key : currentKeys) { - refreshKey(key); } + + return currentKeys; } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 06560e6b13..20f5ecce8f 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -228,7 +228,7 @@ public class ImageNode extends AbstractContentNode { if (parent.getParent().getId() == getContent().getId()) { Children children = getChildren(); if (children != null) { - ((ContentChildren) children).refreshChildren(); +// ((ContentChildren) children).refreshChildren(); children.getNodesCount(); } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java index f04495a706..66291955a8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,14 +20,20 @@ package org.sleuthkit.autopsy.datamodel; import java.util.Collection; import java.util.Collections; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.datamodel.accounts.Accounts; /** * Children implementation for the root node of a ContentNode tree. Accepts a * list of root Content objects for the tree. */ -public class RootContentChildren extends AbstractContentChildren { +public class RootContentChildren extends Children.Keys { private final Collection contentKeys; + private final CreateAutopsyNodeVisitor createAutopsyNodeVisitor = new CreateAutopsyNodeVisitor(); /** * @param contentKeys root Content objects for the Node tree @@ -56,4 +62,120 @@ public class RootContentChildren extends AbstractContentChildren { public void refreshContentKeys() { contentKeys.forEach(this::refreshKey); } + + @Override + protected Node[] createNodes(Object key) { + if (key instanceof AutopsyVisitableItem) { + return new Node[] {((AutopsyVisitableItem)key).accept(createAutopsyNodeVisitor)}; + } else { + return null; + } + } + + /** + * Gets a DisplayableItemNode for use as a subtree root node for the Autopsy + * tree view from each type of AutopsyVisitableItem visited. There are + * AutopsyVisitableItems for the Data Sources, Views, Results, and Reports + * subtrees, and for the subtrees of Results (e.g., Extracted Content, Hash + * Set Hits, etc.). + */ + static class CreateAutopsyNodeVisitor extends AutopsyItemVisitor.Default { + + @Override + public ExtractedContent.RootNode visit(ExtractedContent ec) { + return ec.new RootNode(ec.getSleuthkitCase()); + } + + @Override + public AbstractNode visit(FileTypesByExtension sf) { + return sf.new FileTypesByExtNode(sf.getSleuthkitCase(), null); + } + + @Override + public AbstractNode visit(RecentFiles rf) { + return new RecentFilesNode(rf.getSleuthkitCase()); + } + + @Override + public AbstractNode visit(DeletedContent dc) { + return new DeletedContent.DeletedContentsNode(dc.getSleuthkitCase(), dc.filteringDataSourceObjId()); + } + + @Override + public AbstractNode visit(FileSize dc) { + return new FileSize.FileSizeRootNode(dc.getSleuthkitCase(), dc.filteringDataSourceObjId()); + } + + @Override + public AbstractNode visit(KeywordHits kh) { + return kh.new RootNode(); + } + + @Override + public AbstractNode visit(HashsetHits hh) { + return hh.new RootNode(); + } + + @Override + public AbstractNode visit(InterestingHits ih) { + return ih.new RootNode(); + } + + @Override + public AbstractNode visit(EmailExtracted ee) { + return ee.new RootNode(); + } + + @Override + public AbstractNode visit(Tags tagsNodeKey) { + return tagsNodeKey.new RootNode(tagsNodeKey.filteringDataSourceObjId()); + } + + @Override + public AbstractNode visit(DataSources i) { + return new DataSourcesNode(i.filteringDataSourceObjId()); + } + + @Override + public AbstractNode visit(DataSourceGrouping datasourceGrouping) { + return new DataSourceGroupingNode(datasourceGrouping.getDataSource()); + } + + @Override + public AbstractNode visit(Views v) { + return new ViewsNode(v.getSleuthkitCase(), v.filteringDataSourceObjId()); + } + + @Override + public AbstractNode visit(Results results) { + return new ResultsNode(results.getSleuthkitCase(), results.filteringDataSourceObjId() ); + } + + @Override + public AbstractNode visit(FileTypes ft) { + return ft.new FileTypesNode(); + } + + @Override + public AbstractNode visit(Reports reportsItem) { + return new Reports.ReportsListNode(); + } + + @Override + public AbstractNode visit(Accounts accountsItem) { + return accountsItem.new AccountsRootNode(); + } + + @Override + protected AbstractNode defaultVisit(AutopsyVisitableItem di) { + throw new UnsupportedOperationException( + NbBundle.getMessage(this.getClass(), + "AbstractContentChildren.createAutopsyNodeVisitor.exception.noNodeMsg")); + } + + @Override + public AbstractNode visit(FileTypesByMimeType ftByMimeTypeItem) { + return ftByMimeTypeItem.new ByMimeTypeNode(); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java index 9612441910..429d483ea6 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java @@ -109,7 +109,7 @@ public class VolumeNode extends AbstractContentNode { if (parent.getParent().getId() == getContent().getId()) { Children children = getChildren(); if (children != null) { - ((ContentChildren) children).refreshChildren(); +// ((ContentChildren) children).refreshChildren(); children.getNodesCount(); } } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index c375c7f02b..69df43d1e8 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -320,17 +320,17 @@ public class DataResultFilterNode extends FilterNode { @Override protected Node[] createNodes(Node key) { - AbstractFile file = key.getLookup().lookup(AbstractFile.class); - if (file != null) { - if (filterKnown && (file.getKnown() == TskData.FileKnown.KNOWN)) { - // Filter out child nodes that represent known files - return new Node[]{}; - } - if (filterSlack && file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK)) { - // Filter out child nodes that represent slack files - return new Node[]{}; - } - } +// AbstractFile file = key.getLookup().lookup(AbstractFile.class); +// if (file != null) { +// if (filterKnown && (file.getKnown() == TskData.FileKnown.KNOWN)) { +// // Filter out child nodes that represent known files +// return new Node[]{}; +// } +// if (filterSlack && file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK)) { +// // Filter out child nodes that represent slack files +// return new Node[]{}; +// } +// } // filter out all non-message artifacts, if displaying the results from the Data Source tree BlackboardArtifact art = key.getLookup().lookup(BlackboardArtifact.class); diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ViewAssociatedContentAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ViewAssociatedContentAction.java index aefde11f20..b11cd9e603 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ViewAssociatedContentAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ViewAssociatedContentAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,7 @@ import java.awt.event.ActionEvent; import javax.swing.AbstractAction; import org.sleuthkit.autopsy.corecomponents.DataContentTopComponent; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; -import org.sleuthkit.autopsy.datamodel.RootContentChildren; +import org.sleuthkit.autopsy.datamodel.CreateSleuthkitNodeVisitor; import org.sleuthkit.datamodel.Content; /** @@ -30,7 +30,7 @@ import org.sleuthkit.datamodel.Content; */ class ViewAssociatedContentAction extends AbstractAction { - private Content content; + private final Content content; public ViewAssociatedContentAction(String title, BlackboardArtifactNode node) { super(title); @@ -39,6 +39,6 @@ class ViewAssociatedContentAction extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { - DataContentTopComponent.getDefault().setNode(content.accept(new RootContentChildren.CreateSleuthkitNodeVisitor())); + DataContentTopComponent.getDefault().setNode(content.accept(new CreateSleuthkitNodeVisitor())); } } From 823e1b3967d0383de2d0e038572cdf8130fb850c Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 6 May 2019 15:19:32 -0400 Subject: [PATCH 19/57] Moved slack and known file filtering out of DataResultFilterChildren. --- .../datamodel/AbstractContentChildren.java | 2 +- .../autopsy/datamodel/BaseChildFactory.java | 12 ++- .../DataSourcesKnownAndSlackFilter.java | 52 +++++++++++ .../autopsy/datamodel/DeletedContent.java | 2 +- .../sleuthkit/autopsy/datamodel/FileSize.java | 2 +- .../datamodel/FileTypesByExtension.java | 2 +- .../datamodel/FileTypesByMimeType.java | 2 +- ...lter.java => KnownAndSlackFilterBase.java} | 20 ++--- .../datamodel/ViewsKnownAndSlackFilter.java | 52 +++++++++++ .../directorytree/DataResultFilterNode.java | 89 +------------------ 10 files changed, 125 insertions(+), 110 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesKnownAndSlackFilter.java rename Core/src/org/sleuthkit/autopsy/datamodel/{KnownAndSlackFilter.java => KnownAndSlackFilterBase.java} (63%) create mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/ViewsKnownAndSlackFilter.java diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java index 1a01cc0a40..82c7418a7f 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java @@ -31,7 +31,7 @@ abstract class AbstractContentChildren extends BaseChildFacto private final CreateSleuthkitNodeVisitor createSleuthkitNodeVisitor = new CreateSleuthkitNodeVisitor(); AbstractContentChildren(String nodeName) { - super(nodeName); + super(nodeName, new DataSourcesKnownAndSlackFilter<>()); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java index f5f482a604..0685ffc426 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java @@ -41,7 +41,7 @@ import org.sleuthkit.datamodel.Content; */ public abstract class BaseChildFactory extends ChildFactory.Detachable { - private final Predicate filter; + private Predicate filter; private boolean isPageChangeEvent; private boolean isPageSizeChangeEvent; @@ -54,13 +54,19 @@ public abstract class BaseChildFactory extends ChildFactory.D public static Map nodeNameToEventBusMap = new ConcurrentHashMap<>(); public BaseChildFactory(String nodeName) { + /** + * Initialize a no-op filter that always returns true. + */ + this(nodeName, x -> true); + } + + public BaseChildFactory(String nodeName, Predicate filter) { pagingSupport = new PagingSupport(nodeName); pagingSupport.initialize(); isPageChangeEvent = false; isPageSizeChangeEvent = false; - filter = new KnownAndSlackFilter<>(); + this.filter = filter; } - @Override protected void addNotify() { onAdd(); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesKnownAndSlackFilter.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesKnownAndSlackFilter.java new file mode 100644 index 0000000000..5fbff564b5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesKnownAndSlackFilter.java @@ -0,0 +1,52 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datamodel; + +import java.util.prefs.PreferenceChangeEvent; +import org.sleuthkit.autopsy.core.UserPreferences; +import static org.sleuthkit.autopsy.datamodel.KnownAndSlackFilterBase.filterKnown; +import static org.sleuthkit.autopsy.datamodel.KnownAndSlackFilterBase.filterSlack; +import org.sleuthkit.datamodel.Content; + +/** + * Known and Slack filter for Data Sources section of the tree. + * + * @param + */ +class DataSourcesKnownAndSlackFilter extends KnownAndSlackFilterBase { + + static { + /** + * Watch for user preference changes and update variables inherited from + * our parent. The actual filtering is provided by our parent class. + */ + UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> { + if (evt.getKey().equals(UserPreferences.HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE)) { + filterKnown = UserPreferences.hideKnownFilesInDataSourcesTree(); + } else if (evt.getKey().equals(UserPreferences.HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE)) { + filterSlack = UserPreferences.hideSlackFilesInDataSourcesTree(); + } + }); + } + + DataSourcesKnownAndSlackFilter() { + filterKnown = UserPreferences.hideKnownFilesInDataSourcesTree(); + filterSlack = UserPreferences.hideSlackFilesInDataSourcesTree(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java index 435364803d..25069d9564 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java @@ -367,7 +367,7 @@ public class DeletedContent implements AutopsyVisitableItem { private final long datasourceObjId; DeletedContentChildren(DeletedContent.DeletedContentFilter filter, SleuthkitCase skCase, Observable o, long datasourceObjId) { - super(filter.getName()); + super(filter.getName(), new ViewsKnownAndSlackFilter<>()); this.skCase = skCase; this.filter = filter; this.notifier = o; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java index 78d38259f3..e9c49c596e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java @@ -377,7 +377,7 @@ public class FileSize implements AutopsyVisitableItem { * added to case */ FileSizeChildren(FileSizeFilter filter, SleuthkitCase skCase, Observable o, long dsObjId) { - super(filter.getName()); + super(filter.getName(), new ViewsKnownAndSlackFilter<>()); this.skCase = skCase; this.filter = filter; this.notifier = o; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 27e0705d08..152e8a6bac 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -392,7 +392,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { * data to display */ private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o, String nodeName) { - super(nodeName); + super(nodeName, new ViewsKnownAndSlackFilter<>()); this.filter = filter; this.skCase = skCase; notifier = o; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 49f586c7e8..1e4f61fc87 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -450,7 +450,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi private final String mimeType; private MediaSubTypeNodeChildren(String mimeType) { - super(mimeType); + super(mimeType, new ViewsKnownAndSlackFilter<>()); addObserver(this); this.mimeType = mimeType; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KnownAndSlackFilter.java b/Core/src/org/sleuthkit/autopsy/datamodel/KnownAndSlackFilterBase.java similarity index 63% rename from Core/src/org/sleuthkit/autopsy/datamodel/KnownAndSlackFilter.java rename to Core/src/org/sleuthkit/autopsy/datamodel/KnownAndSlackFilterBase.java index 5c97a5a044..5f5838db42 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KnownAndSlackFilter.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KnownAndSlackFilterBase.java @@ -19,39 +19,31 @@ package org.sleuthkit.autopsy.datamodel; import java.util.function.Predicate; -import org.openide.util.Exceptions; -import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; /** * Predicate that can be used to filter known and/or slack files from * Content collections based on user preferences. */ -class KnownAndSlackFilter implements Predicate { +abstract class KnownAndSlackFilterBase implements Predicate { + protected static boolean filterKnown; + protected static boolean filterSlack; @Override public boolean test(T t) { AbstractFile af = null; - if (t instanceof BlackboardArtifact) { - try { - af = ((BlackboardArtifact) (t)).getSleuthkitCase().getAbstractFileById(((BlackboardArtifact) t).getObjectID()); - } catch (TskCoreException ex) { - Exceptions.printStackTrace(ex); - } - } else if (t instanceof AbstractFile) { + if (t instanceof AbstractFile) { af = (AbstractFile) t; } if (af != null) { - if (af.getKnown() == TskData.FileKnown.KNOWN && UserPreferences.hideKnownFilesInViewsTree()) { + if (af.getKnown() == TskData.FileKnown.KNOWN && filterKnown) { return false; } - if (af.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) && UserPreferences.hideSlackFilesInViewsTree()) { + if (af.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) && filterSlack) { return false; } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ViewsKnownAndSlackFilter.java b/Core/src/org/sleuthkit/autopsy/datamodel/ViewsKnownAndSlackFilter.java new file mode 100644 index 0000000000..b8f41af22f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ViewsKnownAndSlackFilter.java @@ -0,0 +1,52 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit 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.datamodel; + +import java.util.prefs.PreferenceChangeEvent; +import org.sleuthkit.autopsy.core.UserPreferences; +import static org.sleuthkit.autopsy.datamodel.KnownAndSlackFilterBase.filterKnown; +import static org.sleuthkit.autopsy.datamodel.KnownAndSlackFilterBase.filterSlack; +import org.sleuthkit.datamodel.Content; + +/** + * Known and Slack filter for Views section of the tree. + * + * @param + */ +class ViewsKnownAndSlackFilter extends KnownAndSlackFilterBase { + + static { + /** + * Watch for user preference changes and update variables inherited from + * our parent. The actual filtering is provided by our parent class. + */ + UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> { + if (evt.getKey().equals(UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE)) { + filterKnown = UserPreferences.hideKnownFilesInDataSourcesTree(); + } else if (evt.getKey().equals(UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE)) { + filterSlack = UserPreferences.hideSlackFilesInDataSourcesTree(); + } + }); + } + + ViewsKnownAndSlackFilter() { + filterKnown = UserPreferences.hideKnownFilesInDataSourcesTree(); + filterSlack = UserPreferences.hideSlackFilesInDataSourcesTree(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index 69df43d1e8..bc5ecae9ce 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -26,8 +26,6 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.logging.Level; -import java.util.prefs.PreferenceChangeEvent; -import java.util.prefs.PreferenceChangeListener; import javax.swing.AbstractAction; import javax.swing.Action; import org.openide.explorer.ExplorerManager; @@ -41,7 +39,6 @@ import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction; import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; -import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType; @@ -80,7 +77,6 @@ import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.LocalFile; import org.sleuthkit.datamodel.LocalDirectory; import org.sleuthkit.datamodel.SlackFile; -import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.VirtualDirectory; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; @@ -96,33 +92,6 @@ public class DataResultFilterNode extends FilterNode { private static final Logger LOGGER = Logger.getLogger(DataResultFilterNode.class.getName()); - private static boolean filterKnownFromDataSources = UserPreferences.hideKnownFilesInDataSourcesTree(); - private static boolean filterKnownFromViews = UserPreferences.hideKnownFilesInViewsTree(); - private static boolean filterSlackFromDataSources = UserPreferences.hideSlackFilesInDataSourcesTree(); - private static boolean filterSlackFromViews = UserPreferences.hideSlackFilesInViewsTree(); - - static { - UserPreferences.addChangeListener(new PreferenceChangeListener() { - @Override - public void preferenceChange(PreferenceChangeEvent evt) { - switch (evt.getKey()) { - case UserPreferences.HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE: - filterKnownFromDataSources = UserPreferences.hideKnownFilesInDataSourcesTree(); - break; - case UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE: - filterKnownFromViews = UserPreferences.hideKnownFilesInViewsTree(); - break; - case UserPreferences.HIDE_SLACK_FILES_IN_DATA_SRCS_TREE: - filterSlackFromDataSources = UserPreferences.hideSlackFilesInDataSourcesTree(); - break; - case UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE: - filterSlackFromViews = UserPreferences.hideSlackFilesInViewsTree(); - break; - } - } - }); - } - static private final DisplayableItemNodeVisitor> getActionsDIV = new GetPopupActionsDisplayableItemNodeVisitor(); private final DisplayableItemNodeVisitor getPreferredActionsDIV = new GetPreferredActionsDisplayableItemNodeVisitor(); @@ -142,24 +111,6 @@ public class DataResultFilterNode extends FilterNode { this.sourceEm = em; } - /** - * Constructs a node used to wrap another node before passing it to the - * result viewers. The wrapper node defines the actions associated with the - * wrapped node and may filter out some of its children. - * - * @param node The node to wrap. - * @param em The ExplorerManager for the component that is creating - * the node. - * @param filterKnown Whether or not to filter out children that represent - * known files. - * @param filterSlack Whether or not to filter out children that represent - * virtual slack space files. - */ - private DataResultFilterNode(Node node, ExplorerManager em, boolean filterKnown, boolean filterSlack) { - super(node, new DataResultFilterChildren(node, em, filterKnown, filterSlack)); - this.sourceEm = em; - } - /** * Right click action for the nodes that we want to pass to the directory * table and the output view. @@ -281,9 +232,6 @@ public class DataResultFilterNode extends FilterNode { private static class DataResultFilterChildren extends FilterNode.Children { private final ExplorerManager sourceEm; - - private boolean filterKnown; - private boolean filterSlack; private boolean filterArtifacts; // display message artifacts in the DataSource subtree /** @@ -293,45 +241,11 @@ public class DataResultFilterNode extends FilterNode { super(arg); this.filterArtifacts = false; - switch (SelectionContext.getSelectionContext(arg)) { - case DATA_SOURCES: - filterSlack = filterSlackFromDataSources; - filterKnown = filterKnownFromDataSources; - filterArtifacts = true; - break; - case VIEWS: - filterSlack = filterSlackFromViews; - filterKnown = filterKnownFromViews; - break; - default: - filterSlack = false; - filterKnown = false; - break; - } - this.sourceEm = sourceEm; - } - - private DataResultFilterChildren(Node arg, ExplorerManager sourceEm, boolean filterKnown, boolean filterSlack) { - super(arg); - this.filterKnown = filterKnown; - this.filterSlack = filterSlack; this.sourceEm = sourceEm; } @Override protected Node[] createNodes(Node key) { -// AbstractFile file = key.getLookup().lookup(AbstractFile.class); -// if (file != null) { -// if (filterKnown && (file.getKnown() == TskData.FileKnown.KNOWN)) { -// // Filter out child nodes that represent known files -// return new Node[]{}; -// } -// if (filterSlack && file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK)) { -// // Filter out child nodes that represent slack files -// return new Node[]{}; -// } -// } - // filter out all non-message artifacts, if displaying the results from the Data Source tree BlackboardArtifact art = key.getLookup().lookup(BlackboardArtifact.class); if (art != null @@ -341,9 +255,8 @@ public class DataResultFilterNode extends FilterNode { return new Node[]{}; } - return new Node[]{new DataResultFilterNode(key, sourceEm, filterKnown, filterSlack)}; + return new Node[]{new DataResultFilterNode(key, sourceEm)}; } - } @NbBundle.Messages("DataResultFilterNode.viewSourceArtifact.text=View Source Result") From 5dcbe8ad4ed01d45caeacbc6facf20a136b2639e Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 6 May 2019 15:46:33 -0400 Subject: [PATCH 20/57] Fix for bug in ViewsKnownAndSlackFilter --- .../autopsy/datamodel/ViewsKnownAndSlackFilter.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ViewsKnownAndSlackFilter.java b/Core/src/org/sleuthkit/autopsy/datamodel/ViewsKnownAndSlackFilter.java index b8f41af22f..c06ba53bf9 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ViewsKnownAndSlackFilter.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ViewsKnownAndSlackFilter.java @@ -38,15 +38,15 @@ class ViewsKnownAndSlackFilter extends KnownAndSlackFilterBas */ UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> { if (evt.getKey().equals(UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE)) { - filterKnown = UserPreferences.hideKnownFilesInDataSourcesTree(); + filterKnown = UserPreferences.hideKnownFilesInViewsTree(); } else if (evt.getKey().equals(UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE)) { - filterSlack = UserPreferences.hideSlackFilesInDataSourcesTree(); + filterSlack = UserPreferences.hideSlackFilesInViewsTree(); } }); } ViewsKnownAndSlackFilter() { - filterKnown = UserPreferences.hideKnownFilesInDataSourcesTree(); - filterSlack = UserPreferences.hideSlackFilesInDataSourcesTree(); + filterKnown = UserPreferences.hideKnownFilesInViewsTree(); + filterSlack = UserPreferences.hideSlackFilesInViewsTree(); } } From aaa22f80d8b0b74b91e824c313878140ee93fb66 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Tue, 7 May 2019 12:54:31 -0400 Subject: [PATCH 21/57] Implemented the second pass algorithm to improve mime type accuracy --- .../modules/filetypeid/FileTypeDetector.java | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java index dace794e85..405d3ed85d 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java @@ -58,7 +58,7 @@ public class FileTypeDetector { * @return A list of all detectable file types. * * @throws FileTypeDetectorInitException If an error occurs while assembling - * the list of types + * the list of types */ public static synchronized SortedSet getDetectedTypes() throws FileTypeDetectorInitException { TreeSet detectedTypes = new TreeSet<>((String string1, String string2) -> { @@ -109,7 +109,9 @@ public class FileTypeDetector { * Tika, and Autopsy file type definitions take precendence over Tika. * * @throws FileTypeDetectorInitException If an initialization error occurs, - * e.g., user-defined file type definitions exist but cannot be loaded. + * e.g., user-defined file type + * definitions exist but cannot be + * loaded. */ public FileTypeDetector() throws FileTypeDetectorInitException { try { @@ -139,7 +141,7 @@ public class FileTypeDetector { * user-defined MIME type by this detector. * * @param customTypes - * @param mimeType The MIME type name (e.g., "text/html"). + * @param mimeType The MIME type name (e.g., "text/html"). * * @return True or false. */ @@ -170,7 +172,7 @@ public class FileTypeDetector { * @param file The file to test. * * @return A MIME type name. If file type could not be detected, or results - * were uncertain, octet-stream is returned. + * were uncertain, octet-stream is returned. * * */ @@ -223,7 +225,7 @@ public class FileTypeDetector { ReadContentInputStream stream = new ReadContentInputStream(file); try (TikaInputStream tikaInputStream = TikaInputStream.get(stream)) { - String tikaType = tika.detect(tikaInputStream, file.getName()); + String tikaType = tika.detect(tikaInputStream); /* * Remove the Tika suffix from the MIME type name. @@ -233,6 +235,23 @@ public class FileTypeDetector { * Remove the optional parameter from the MIME type. */ mimeType = removeOptionalParameter(mimeType); + + /* + * If Tika recognizes the file signature, then use the file + * name to refine the type. In short, this is to exclude the + * mime types that are determined solely by file extension. + * More details in JIRA-4871. + */ + if (!mimeType.equals(MimeTypes.OCTET_STREAM)) { + ReadContentInputStream secondPassStream = new ReadContentInputStream(file); + try (TikaInputStream secondPassTikaStream = TikaInputStream.get(secondPassStream)) { + tikaType = tika.detect(secondPassTikaStream, file.getName()); + mimeType = tikaType.replace("tika-", ""); //NON-NLS + mimeType = removeOptionalParameter(mimeType); + } catch (Exception ex) { + throw ex; + } + } /** * We cannot trust Tika's audio/mpeg mimetype. Lets verify the @@ -275,6 +294,7 @@ public class FileTypeDetector { * first 4 bits. * * @param x byte + * * @return Flag indicating the byte if 0xFF */ private boolean byteIs0xFF(byte x) { @@ -284,9 +304,10 @@ public class FileTypeDetector { /** * Retrieves the first N bytes from a file. * - * @param file Abstract file to read + * @param file Abstract file to read * @param offset Offset to begin reading - * @param n Number of bytes to read + * @param n Number of bytes to read + * * @return Byte array of size n * * @throws TskCoreException @@ -371,7 +392,7 @@ public class FileTypeDetector { * Constructs an exception to throw if an initialization error occurs, * e.g., user-defined file type definitions exist but cannot be loaded. * - * @param message The exception message, + * @param message The exception message, * @param throwable The underlying cause of the exception. */ FileTypeDetectorInitException(String message, Throwable throwable) { @@ -409,7 +430,7 @@ public class FileTypeDetector { * @return A MIME type name. * * @throws TskCoreException if detection is required and there is a problem - * writing the result to the case database. + * writing the result to the case database. * @deprecated Use getMIMEType instead, and call AbstractFile.setMIMEType * and AbstractFile.save to save the result to the file object and the * database. @@ -429,10 +450,10 @@ public class FileTypeDetector { * @param file The file. * * @return A MIME type name. If file type could not be detected or results - * were uncertain, octet-stream is returned. + * were uncertain, octet-stream is returned. * * @throws TskCoreException if detection is required and there is a problem - * writing the result to the case database. + * writing the result to the case database. * * @deprecated Use getMIMEType instead, and call AbstractFile.setMIMEType * and AbstractFile.save to save the result to the file object and the @@ -453,7 +474,7 @@ public class FileTypeDetector { * @param file The file to test. * * @return A MIME type name. If file type could not be detected or results - * were uncertain, octet-stream is returned. + * were uncertain, octet-stream is returned. * * @throws TskCoreException * @deprecated Use getMIMEType instead. From fb6539c49b6c1d00b050c22d693a3e5515cdbbb1 Mon Sep 17 00:00:00 2001 From: esaunders Date: Tue, 7 May 2019 15:08:36 -0400 Subject: [PATCH 22/57] Get refresh working from directory tree. --- .../datamodel/AbstractAbstractFileNode.java | 13 +++++------ .../datamodel/AbstractContentNode.java | 1 - .../autopsy/datamodel/BaseChildFactory.java | 14 ++++++++++++ .../autopsy/datamodel/ImageNode.java | 22 ++++++++++++------- .../autopsy/datamodel/VolumeNode.java | 22 ++++++++++++------- 5 files changed, 48 insertions(+), 24 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index 0cafd68bc7..c15df3ba55 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.datamodel; +import com.google.common.eventbus.EventBus; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -55,6 +56,7 @@ import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score; import org.sleuthkit.autopsy.coreutils.Logger; import static org.sleuthkit.autopsy.datamodel.Bundle.*; import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*; +import org.sleuthkit.autopsy.datamodel.BaseChildFactory.RefreshKeysEvent; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleContentEvent; import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException; @@ -164,17 +166,14 @@ public abstract class AbstractAbstractFileNode extends A if (getContent().getId() == newContent.getId()) { // If so, refresh our children. try { - Children parentsChildren = getParentNode().getChildren(); // We only want to refresh our parents children if we are in the // data sources branch of the tree. The parent nodes in other // branches of the tree (e.g. File Types and Deleted Files) do // not need to be refreshed. - -// TODO: How will this work with ChildFactory approach? -// if (parentsChildren instanceof ContentChildren) { -// ((ContentChildren) parentsChildren).refreshChildren(); -// parentsChildren.getNodesCount(); -// } + EventBus bus = BaseChildFactory.nodeNameToEventBusMap.get(getParentNode().getName()); + if (bus != null) { + bus.post(new RefreshKeysEvent()); + } } catch (NullPointerException ex) { // Skip } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index 4f2010fd94..4f6f2e5f47 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -66,7 +66,6 @@ public abstract class AbstractContentNode extends ContentNode * @param lookup The Lookup object for the node. */ AbstractContentNode(T content, Lookup lookup) { - //TODO consider child factory for the content children super(Children.create(new ContentChildren(content), true), lookup); this.content = content; //super.setName(ContentUtils.getSystemName(content)); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java index 0685ffc426..cae42aea27 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java @@ -67,6 +67,7 @@ public abstract class BaseChildFactory extends ChildFactory.D isPageSizeChangeEvent = false; this.filter = filter; } + @Override protected void addNotify() { onAdd(); @@ -117,6 +118,12 @@ public abstract class BaseChildFactory extends ChildFactory.D return true; } + /** + * Event used to trigger recreation of the keys. + */ + public static class RefreshKeysEvent { + } + /** * Event used to let subscribers know that the user has navigated to a * different page. @@ -283,5 +290,12 @@ public abstract class BaseChildFactory extends ChildFactory.D refresh(true); } } + + @Subscribe + private void subscribeToRefreshKeys(RefreshKeysEvent event) { + if (event != null) { + refresh(true); + } + } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 20f5ecce8f..a761099062 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.datamodel; +import com.google.common.eventbus.EventBus; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.sql.ResultSet; @@ -28,7 +29,6 @@ import java.util.EnumSet; import java.util.List; import java.util.logging.Level; import javax.swing.Action; -import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; @@ -224,12 +224,18 @@ public class ImageNode extends AbstractContentNode { if (parent != null) { // Is this a new carved file? if (parent.getName().equals(VirtualDirectory.NAME_CARVED)) { - // Was this new carved file produced from this image? - if (parent.getParent().getId() == getContent().getId()) { - Children children = getChildren(); - if (children != null) { -// ((ContentChildren) children).refreshChildren(); - children.getNodesCount(); + // Is this new carved file for this data source? + if (newContent.getDataSource().getId() == getContent().getDataSource().getId()) { + // Find the image (if any) associated with the new content and + // trigger a refresh if it matches the image wrapped by this node. + while ((parent = parent.getParent()) != null) { + if (parent.getId() == getContent().getId()) { + EventBus bus = BaseChildFactory.nodeNameToEventBusMap.get(getName()); + if (bus != null) { + bus.post(new BaseChildFactory.RefreshKeysEvent()); + } + break; + } } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java index 429d483ea6..02a65a569c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,13 +18,13 @@ */ package org.sleuthkit.autopsy.datamodel; +import com.google.common.eventbus.EventBus; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import javax.swing.Action; -import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; @@ -105,12 +105,18 @@ public class VolumeNode extends AbstractContentNode { if (parent != null) { // Is this a new carved file? if (parent.getName().equals(VirtualDirectory.NAME_CARVED)) { - // Was this new carved file produced from this volume? - if (parent.getParent().getId() == getContent().getId()) { - Children children = getChildren(); - if (children != null) { -// ((ContentChildren) children).refreshChildren(); - children.getNodesCount(); + // Is this new carved file for this data source? + if (newContent.getDataSource().getId() == getContent().getDataSource().getId()) { + // Find the volume (if any) associated with the new content and + // trigger a refresh if it matches the volume wrapped by this node. + while ((parent = parent.getParent()) != null) { + if (parent.getId() == getContent().getId()) { + EventBus bus = BaseChildFactory.nodeNameToEventBusMap.get(getName()); + if (bus != null) { + bus.post(new BaseChildFactory.RefreshKeysEvent()); + } + break; + } } } } From 5a15583716e587d74d05fdbcc295cd2221741dbc Mon Sep 17 00:00:00 2001 From: esaunders Date: Tue, 7 May 2019 15:14:05 -0400 Subject: [PATCH 23/57] Made PagingSupport nested class private based on feedback. --- Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java index cae42aea27..71c7542a5e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java @@ -177,7 +177,7 @@ public abstract class BaseChildFactory extends ChildFactory.D * Class that supplies paging related functionality to the base child * factory class. */ - class PagingSupport { + private class PagingSupport { private final String nodeName; private int pageSize; From b1158de9cca712dd2092f2596e7e79eabba76ce7 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 8 May 2019 13:15:51 -0400 Subject: [PATCH 24/57] Added code to initalize the start and end date filters to earliest and latest in case --- .../autopsy/communications/FiltersPanel.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 56c8be50a7..2eed321777 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -22,8 +22,13 @@ import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.Subscribe; import java.awt.event.ItemListener; import java.beans.PropertyChangeListener; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.Instant; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.ZoneOffset; import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; @@ -34,6 +39,7 @@ import java.util.logging.Level; import java.util.stream.Collectors; import javax.swing.JCheckBox; import javax.swing.JPanel; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; @@ -46,6 +52,7 @@ import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.CaseDbAccessManager.CaseDbAccessQueryCallback; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter; import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter; @@ -150,6 +157,29 @@ final public class FiltersPanel extends JPanel { applyFiltersButton.addActionListener(e -> applyFilters()); refreshButton.addActionListener(e -> applyFilters()); + + try { + String queryString = "max(date_time) as max, min(date_time) as min from account_relationships"; // NON-NLS + Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select(queryString, new FilterPanelQueryCallback() { + @Override + public void process(ResultSet rs) { + try { + if (rs.next()) { + int startDate = rs.getInt("min"); // NON-NLS + int endData = rs.getInt("max"); // NON-NLS + + startDatePicker.setDate(LocalDateTime.ofInstant(Instant.ofEpochSecond(startDate), Utils.getUserPreferredZoneId()).toLocalDate()); + endDatePicker.setDate(LocalDateTime.ofInstant(Instant.ofEpochSecond(endData), Utils.getUserPreferredZoneId()).toLocalDate()); + } + } catch (SQLException ex) { + logger.log(Level.WARNING, "Unable to set filter date pickers due to SQL exception", ex); //NON-NLS + } + } + + }); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.SEVERE, "Unable to set filter date pickers due to exception", ex); //NON-NLS + } } /** @@ -788,4 +818,14 @@ final public class FiltersPanel extends JPanel { private final javax.swing.JButton unCheckAllAccountTypesButton = new javax.swing.JButton(); private final javax.swing.JButton unCheckAllDevicesButton = new javax.swing.JButton(); // End of variables declaration//GEN-END:variables + + class FilterPanelQueryCallback implements CaseDbAccessQueryCallback { + + @Override + public void process(ResultSet rs) { + + } + + } + } From 39bc180f342ba6e5f9c6b9c5ecb83fce1466d547 Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 8 May 2019 14:10:57 -0400 Subject: [PATCH 25/57] Added static methods to BaseChildFactory for registering with and posting to the event bus. --- .../corecomponents/DataResultViewerTable.java | 63 ++++++++--------- .../datamodel/AbstractAbstractFileNode.java | 10 ++- .../autopsy/datamodel/BaseChildFactory.java | 67 ++++++++++++++++--- .../autopsy/datamodel/ImageNode.java | 9 ++- .../autopsy/datamodel/VolumeNode.java | 12 ++-- 5 files changed, 98 insertions(+), 63 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 0d2c831528..e23af3b905 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.corecomponents; -import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import java.awt.Component; import java.awt.Cursor; @@ -208,7 +207,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { private void initializePagingSupport() { if (pagingSupport == null) { - pagingSupport = new PagingSupport(); + pagingSupport = new PagingSupport(""); } // Start out with paging controls invisible @@ -312,19 +311,12 @@ public class DataResultViewerTable extends AbstractDataResultViewer { * Check to see if we have previously created a paging support * class for this node. */ - pagingSupport = nodeNameToPagingSupportMap.get(rootNode.getName()); + String nodeName = rootNode.getName(); + pagingSupport = nodeNameToPagingSupportMap.get(nodeName); if (pagingSupport == null) { - pagingSupport = new PagingSupport(); + pagingSupport = new PagingSupport(nodeName); + nodeNameToPagingSupportMap.put(nodeName, pagingSupport); } - - /** - * Get the event bus to use when communicating with the child - * factory for this node and register with the bus. - */ - EventBus bus = BaseChildFactory.nodeNameToEventBusMap.get(rootNode.getName()); - pagingSupport.registerWithEventBus(bus); - - nodeNameToPagingSupportMap.put(rootNode.getName(), pagingSupport); pagingSupport.updateControls(); rootNode.addNodeListener(new NodeListener() { @@ -786,24 +778,19 @@ public class DataResultViewerTable extends AbstractDataResultViewer { private int currentPage; private int totalPages; - private EventBus eb; + private final String nodeName; - PagingSupport() { + PagingSupport(String nodeName) { currentPage = 1; totalPages = 0; - eb = null; + this.nodeName = nodeName; + initialize(); } - void registerWithEventBus(EventBus bus) { - if (eb != null && eb.equals(bus)) { - return; + private void initialize() { + if (!nodeName.isEmpty()) { + BaseChildFactory.register(nodeName, this); } - - if (bus != null) { - eb = bus; - eb.register(this); - } - updateControls(); } @@ -844,10 +831,12 @@ public class DataResultViewerTable extends AbstractDataResultViewer { * occurred. */ void postPageChangeEvent() { - if (eb != null) { - eb.post(new PageChangeEvent(currentPage)); - DataResultViewerTable.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try { + BaseChildFactory.post(nodeName, new PageChangeEvent(currentPage)); + } catch (BaseChildFactory.NoSuchEventBusException ex) { + LOGGER.log(Level.WARNING, "Failed to post page change event.", ex); //NON-NLS } + DataResultViewerTable.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); updateControls(); } @@ -856,15 +845,17 @@ public class DataResultViewerTable extends AbstractDataResultViewer { * occurred. */ void postPageSizeChangeEvent() { - if (eb != null) { - // Reset page variables when page size changes - currentPage = 1; - totalPages = 0; + // Reset page variables when page size changes + currentPage = 1; + totalPages = 0; - if (this == pagingSupport) { - updateControls(); - } - eb.post(new PageSizeChangeEvent(UserPreferences.getResultsTablePageSize())); + if (this == pagingSupport) { + updateControls(); + } + try { + BaseChildFactory.post(nodeName, new PageSizeChangeEvent(UserPreferences.getResultsTablePageSize())); + } catch (BaseChildFactory.NoSuchEventBusException ex) { + LOGGER.log(Level.WARNING, "Failed to post page size change event.", ex); //NON-NLS } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index c15df3ba55..fa4529e44b 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.datamodel; -import com.google.common.eventbus.EventBus; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -35,7 +34,6 @@ import java.util.stream.Collectors; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; -import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.WeakListeners; @@ -56,6 +54,7 @@ import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score; import org.sleuthkit.autopsy.coreutils.Logger; import static org.sleuthkit.autopsy.datamodel.Bundle.*; import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*; +import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException; import org.sleuthkit.autopsy.datamodel.BaseChildFactory.RefreshKeysEvent; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleContentEvent; @@ -170,12 +169,11 @@ public abstract class AbstractAbstractFileNode extends A // data sources branch of the tree. The parent nodes in other // branches of the tree (e.g. File Types and Deleted Files) do // not need to be refreshed. - EventBus bus = BaseChildFactory.nodeNameToEventBusMap.get(getParentNode().getName()); - if (bus != null) { - bus.post(new RefreshKeysEvent()); - } + BaseChildFactory.post(getParentNode().getName(), new RefreshKeysEvent()); } catch (NullPointerException ex) { // Skip + } catch (NoSuchEventBusException ex) { + logger.log(Level.WARNING, "Failed to post key refresh event", ex); //NON-NLS } } } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java index 71c7542a5e..4a1147494a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java @@ -27,9 +27,12 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.prefs.PreferenceChangeEvent; import java.util.stream.Collectors; import org.openide.nodes.ChildFactory; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.datamodel.Content; @@ -41,6 +44,8 @@ import org.sleuthkit.datamodel.Content; */ public abstract class BaseChildFactory extends ChildFactory.Detachable { + private static final Logger logger = Logger.getLogger(BaseChildFactory.class.getName()); + private Predicate filter; private boolean isPageChangeEvent; private boolean isPageSizeChangeEvent; @@ -51,7 +56,50 @@ public abstract class BaseChildFactory extends ChildFactory.D * This static map is used to facilitate communication between the UI and * the child factory. */ - public static Map nodeNameToEventBusMap = new ConcurrentHashMap<>(); + private static Map nodeNameToEventBusMap = new ConcurrentHashMap<>(); + + @Messages({ + "# {0} - node name", "BaseChildFactory.NoSuchEventBusException.message=No event bus for node: {0}" + }) + public static class NoSuchEventBusException extends Exception { + + public NoSuchEventBusException(String nodeName) { + super(Bundle.BaseChildFactory_NoSuchEventBusException_message(nodeName)); + } + } + + /** + * Register the given subscriber for the given node name. Will create the + * event bus for the given node name if it does not exist. + * + * @param nodeName The name of the node. + * @param subscriber The subscriber to register. + */ + public static void register(String nodeName, Object subscriber) { + EventBus bus = nodeNameToEventBusMap.get(nodeName); + if (bus == null) { + bus = new EventBus(nodeName); + nodeNameToEventBusMap.put(nodeName, bus); + } + bus.register(subscriber); + } + + /** + * Post the given event for the given node name. + * + * @param nodeName The name of the node. + * @param event The event to post. + * + * @throws + * org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException + */ + public static void post(String nodeName, Object event) throws NoSuchEventBusException { + EventBus bus = nodeNameToEventBusMap.get(nodeName); + if (bus == null) { + throw new NoSuchEventBusException(nodeName); + } + bus.post(event); + } public BaseChildFactory(String nodeName) { /** @@ -183,7 +231,6 @@ public abstract class BaseChildFactory extends ChildFactory.D private int pageSize; private int currentPage; private List> pages; - private EventBus bus; /** * Construct PagingSupport instance for the given node name. @@ -211,13 +258,7 @@ public abstract class BaseChildFactory extends ChildFactory.D } }); - if (nodeNameToEventBusMap.containsKey(nodeName)) { - bus = nodeNameToEventBusMap.get(nodeName); - } else { - bus = new EventBus(nodeName); - nodeNameToEventBusMap.put(bus.identifier(), bus); - } - bus.register(this); + register(nodeName, this); } /** @@ -247,8 +288,12 @@ public abstract class BaseChildFactory extends ChildFactory.D */ pages = Lists.partition(keys, pageSize > 0 ? pageSize : keys.size()); if (pages.size() != oldPageCount) { - // Number of pages has changed so we need to send out a notification. - bus.post(new PageCountChangeEvent(pages.size())); + try { + // Number of pages has changed so we need to send out a notification. + post(nodeName, new PageCountChangeEvent(pages.size())); + } catch (NoSuchEventBusException ex) { + logger.log(Level.WARNING, "Failed to post page change event.", ex); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index a761099062..584a405fcc 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.datamodel; -import com.google.common.eventbus.EventBus; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.sql.ResultSet; @@ -47,6 +46,7 @@ import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.VirtualDirectory; +import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException; /** * This class is used to represent the "Node" for the image. The children of @@ -230,10 +230,7 @@ public class ImageNode extends AbstractContentNode { // trigger a refresh if it matches the image wrapped by this node. while ((parent = parent.getParent()) != null) { if (parent.getId() == getContent().getId()) { - EventBus bus = BaseChildFactory.nodeNameToEventBusMap.get(getName()); - if (bus != null) { - bus.post(new BaseChildFactory.RefreshKeysEvent()); - } + BaseChildFactory.post(getName(), new BaseChildFactory.RefreshKeysEvent()); break; } } @@ -242,6 +239,8 @@ public class ImageNode extends AbstractContentNode { } } catch (TskCoreException ex) { // Do nothing. + } catch (NoSuchEventBusException ex) { + logger.log(Level.WARNING, "Failed to post key refresh event.", ex); // NON-NLS } } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { if (evt.getNewValue() == null) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java index 02a65a569c..d25b50d59f 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java @@ -18,16 +18,18 @@ */ package org.sleuthkit.autopsy.datamodel; -import com.google.common.eventbus.EventBus; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; +import java.util.logging.Level; import javax.swing.Action; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException; import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -43,6 +45,7 @@ import org.sleuthkit.autopsy.directorytree.FileSystemDetailsAction; * root directory of a file system */ public class VolumeNode extends AbstractContentNode { + private static final Logger logger = Logger.getLogger(VolumeNode.class.getName()); /** * Helper so that the display name and the name used in building the path @@ -111,10 +114,7 @@ public class VolumeNode extends AbstractContentNode { // trigger a refresh if it matches the volume wrapped by this node. while ((parent = parent.getParent()) != null) { if (parent.getId() == getContent().getId()) { - EventBus bus = BaseChildFactory.nodeNameToEventBusMap.get(getName()); - if (bus != null) { - bus.post(new BaseChildFactory.RefreshKeysEvent()); - } + BaseChildFactory.post(getName(), new BaseChildFactory.RefreshKeysEvent()); break; } } @@ -123,6 +123,8 @@ public class VolumeNode extends AbstractContentNode { } } catch (TskCoreException ex) { // Do nothing. + } catch (NoSuchEventBusException ex) { + logger.log(Level.WARNING, eventType, ex); } } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { if (evt.getNewValue() == null) { From df5fcb2eb8d357c1ba1556cc02ec176d7093da8f Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 9 May 2019 09:56:46 -0400 Subject: [PATCH 26/57] Addressed codacy issues --- .../autopsy/communications/FiltersPanel.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 2eed321777..68482db719 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -28,7 +28,6 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; -import java.time.ZoneOffset; import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; @@ -39,7 +38,6 @@ import java.util.logging.Level; import java.util.stream.Collectors; import javax.swing.JCheckBox; import javax.swing.JPanel; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; @@ -819,13 +817,16 @@ final public class FiltersPanel extends JPanel { private final javax.swing.JButton unCheckAllDevicesButton = new javax.swing.JButton(); // End of variables declaration//GEN-END:variables + /** + * A simple class that implements CaseDbAccessQueryCallback. Can be used + * as an anonymous innerclass with the CaseDbAccessManager select function. + */ class FilterPanelQueryCallback implements CaseDbAccessQueryCallback { @Override public void process(ResultSet rs) { - + // Subclasses can implement their own process function. } - } - + } From ca028d478f1f4ce75626915d69c723d31039a57b Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 9 May 2019 16:31:11 -0400 Subject: [PATCH 27/57] Codacy fixes --- .../embeddedfileextractor/PDFAttachmentExtractor.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/PDFAttachmentExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/PDFAttachmentExtractor.java index 7a0747b648..a36b5c365d 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/PDFAttachmentExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/PDFAttachmentExtractor.java @@ -49,7 +49,7 @@ import org.sleuthkit.datamodel.TskData; */ final class PDFAttachmentExtractor { - private static Logger logger = Logger.getLogger(PDFAttachmentExtractor.class.getName()); + private static final Logger logger = Logger.getLogger(PDFAttachmentExtractor.class.getName()); private final AutoDetectParser parser; public PDFAttachmentExtractor() { @@ -176,5 +176,8 @@ final class PDFAttachmentExtractor { throw new IOException(msg); } } + + private ExtractionPreconditions(){ + } } } From 29b3dd07311a7de3cb1e19257883f2f4c1eeab04 Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Fri, 10 May 2019 09:48:58 -0400 Subject: [PATCH 28/57] initial code, not all working --- .../casemodule/LogicalImagerDSProcessor.java | 43 ++++++++++++++++++- .../casemodule/LogicalImagerPanel.form | 2 +- .../casemodule/LogicalImagerPanel.java | 6 ++- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java index 6715997a71..03530b6cdd 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java @@ -19,19 +19,26 @@ package org.sleuthkit.autopsy.casemodule; import java.io.File; +import java.io.IOException; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Calendar; import java.util.List; +import java.util.TimeZone; import java.util.UUID; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.filechooser.FileFilter; +import org.apache.commons.io.FileUtils; +import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProviders; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.events.AutopsyEventException; /** * A Logical Imager data source processor that implements the DataSourceProcessor service @@ -44,6 +51,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; ) public class LogicalImagerDSProcessor implements DataSourceProcessor { + private static final String LOGICAL_IMAGER_DIR = "LogicalImager"; //NON-NLS private final LogicalImagerPanel configPanel; private AddImageTask addImageTask; @@ -132,12 +140,43 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { JOptionPane.showMessageDialog(null, msg, "ERROR", JOptionPane.ERROR_MESSAGE); return; } + // Create the LogicalImager directory under ModuleDirectory + String moduleDirectory = Case.getCurrentCase().getModuleDirectory(); + File logicalImagerDir = Paths.get(moduleDirectory, LOGICAL_IMAGER_DIR).toFile(); + if (!logicalImagerDir.exists()) { + if (!logicalImagerDir.mkdir()) { + // create failed + String msg = "Fail to create directory " + logicalImagerDir; + JOptionPane.showMessageDialog(null, msg, "ERROR", JOptionPane.ERROR_MESSAGE); + return; + } + } + String imagePathParent = imagePath.getParent().toFile().getName(); + File dest = Paths.get(logicalImagerDir.toString(), imagePathParent).toFile(); + if (dest.exists()) { + // directory already exists + String msg = "Directory " + dest.toString() + " already exists"; + JOptionPane.showMessageDialog(null, msg, "ERROR", JOptionPane.ERROR_MESSAGE); + return; + } + File src = imagePath.getParent().toFile(); + try { + configPanel.setMessageLabel("Copying " + src.toString() + " directory to " + dest.toString()); + FileUtils.copyDirectory(src, dest); + configPanel.setMessageLabel(""); + } catch (IOException ex) { + // Copy directory failed + String msg = "Failed to copy directory " + imagePath.getParent().toString() + " to " + dest.toString() ; + JOptionPane.showMessageDialog(null, msg, "ERROR", JOptionPane.ERROR_MESSAGE); + return; + } + String deviceId = UUID.randomUUID().toString(); - String timeZone = "America/New_York"; // TODO: temporary + String timeZone = Calendar.getInstance().getTimeZone().getID(); boolean ignoreFatOrphanFiles = false; run(deviceId, imagePath.toString(), 0, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callback); } - + /** * Adds a "Logical Imager" data source to the case database using a background task in * a separate thread and the given settings instead of those provided by the diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.form index 3afcae45fa..c0888e4ee4 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.form @@ -68,7 +68,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java index 63e7f3f276..d6e9c7d71f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java @@ -237,7 +237,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { .addGroup(layout.createSequentialGroup() .addGap(144, 144, 144) .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 116, javax.swing.GroupLayout.PREFERRED_SIZE)))) - .addContainerGap(60, Short.MAX_VALUE)))) + .addContainerGap(66, Short.MAX_VALUE)))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -448,6 +448,10 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { Path getImagePath() { return choosenImagePath; } + + public void setMessageLabel(String message) { + messageLabel.setText(message); + } @Override public void insertUpdate(DocumentEvent e) { From 2fb757d6427fa3ae0ac1a12717541676831dc871 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Fri, 10 May 2019 10:16:42 -0400 Subject: [PATCH 29/57] Removed unnecessary try block --- .../autopsy/modules/filetypeid/FileTypeDetector.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java index 405d3ed85d..b8746bf371 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java @@ -244,13 +244,10 @@ public class FileTypeDetector { */ if (!mimeType.equals(MimeTypes.OCTET_STREAM)) { ReadContentInputStream secondPassStream = new ReadContentInputStream(file); - try (TikaInputStream secondPassTikaStream = TikaInputStream.get(secondPassStream)) { - tikaType = tika.detect(secondPassTikaStream, file.getName()); - mimeType = tikaType.replace("tika-", ""); //NON-NLS - mimeType = removeOptionalParameter(mimeType); - } catch (Exception ex) { - throw ex; - } + TikaInputStream secondPassTikaStream = TikaInputStream.get(secondPassStream); + tikaType = tika.detect(secondPassTikaStream, file.getName()); + mimeType = tikaType.replace("tika-", ""); //NON-NLS + mimeType = removeOptionalParameter(mimeType); } /** From 39786e2c9257380ac286962f4aea8d1f0d87a452 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Fri, 10 May 2019 10:22:09 -0400 Subject: [PATCH 30/57] Close second stream --- .../autopsy/modules/filetypeid/FileTypeDetector.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java index b8746bf371..0c885472de 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java @@ -244,10 +244,11 @@ public class FileTypeDetector { */ if (!mimeType.equals(MimeTypes.OCTET_STREAM)) { ReadContentInputStream secondPassStream = new ReadContentInputStream(file); - TikaInputStream secondPassTikaStream = TikaInputStream.get(secondPassStream); - tikaType = tika.detect(secondPassTikaStream, file.getName()); - mimeType = tikaType.replace("tika-", ""); //NON-NLS - mimeType = removeOptionalParameter(mimeType); + try (TikaInputStream secondPassTikaStream = TikaInputStream.get(secondPassStream)) { + tikaType = tika.detect(secondPassTikaStream, file.getName()); + mimeType = tikaType.replace("tika-", ""); //NON-NLS + mimeType = removeOptionalParameter(mimeType); + } } /** From de23b6b637a57c77c68a38cd7594a898a5734532 Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 13 May 2019 16:49:08 -0400 Subject: [PATCH 31/57] Go to page should not be enabled if there is only one page. --- .../sleuthkit/autopsy/corecomponents/DataResultViewerTable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index e23af3b905..4b47ae2faf 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -909,7 +909,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { pageNextButton.setEnabled(currentPage != totalPages); pagePrevButton.setEnabled(currentPage != 1); - gotoPageTextField.setEnabled(true); + gotoPageTextField.setEnabled(currentPage != totalPages); gotoPageTextField.setText(""); } } From b35b7c0214f276ac6736f34289a48d70024676f4 Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Tue, 14 May 2019 10:44:58 -0400 Subject: [PATCH 32/57] Validate imageDirPath --- .../casemodule/LogicalImagerDSProcessor.java | 17 +++++++++-------- .../autopsy/casemodule/LogicalImagerPanel.form | 7 +++++-- .../autopsy/casemodule/LogicalImagerPanel.java | 6 +++++- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java index bdc3fef869..b45fa1ff54 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java @@ -46,6 +46,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgress public class LogicalImagerDSProcessor implements DataSourceProcessor { private static final String LOGICAL_IMAGER_DIR = "LogicalImager"; //NON-NLS + private static final String SPARSE_IMAGE_VHD = "sparse_image.vhd"; //NON-NLS private final LogicalImagerPanel configPanel; /* @@ -125,10 +126,10 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { @Override public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { configPanel.storeSettings(); - Path imagePath = configPanel.getImagePath(); - if (!imagePath.toFile().exists()) { + Path imageDirPath = configPanel.getImageDirPath(); + if (!imageDirPath.toFile().exists()) { // TODO: Better ways to detect ejected USB drive? - String msg = imagePath.toString() + " not found.\nUSB drive has been ejected."; + String msg = imageDirPath.toString() + " not found.\nUSB drive has been ejected."; JOptionPane.showMessageDialog(null, msg, "ERROR", JOptionPane.ERROR_MESSAGE); return; } @@ -143,22 +144,22 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { return; } } - String imagePathParent = imagePath.getParent().toFile().getName(); - File dest = Paths.get(logicalImagerDir.toString(), imagePathParent).toFile(); + File dest = Paths.get(logicalImagerDir.toString(), imageDirPath.getFileName().toString()).toFile(); if (dest.exists()) { // directory already exists String msg = "Directory " + dest.toString() + " already exists"; JOptionPane.showMessageDialog(null, msg, "ERROR", JOptionPane.ERROR_MESSAGE); + configPanel.popDownPanel(); return; } - File src = imagePath.getParent().toFile(); + File src = imageDirPath.toFile(); try { configPanel.setMessageLabel("Copying " + src.toString() + " directory to " + dest.toString()); FileUtils.copyDirectory(src, dest); configPanel.setMessageLabel(""); } catch (IOException ex) { // Copy directory failed - String msg = "Failed to copy directory " + imagePath.getParent().toString() + " to " + dest.toString() ; + String msg = "Failed to copy directory " + src.toString() + " to " + dest.toString() ; JOptionPane.showMessageDialog(null, msg, "ERROR", JOptionPane.ERROR_MESSAGE); return; } @@ -166,7 +167,7 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { String deviceId = UUID.randomUUID().toString(); String timeZone = Calendar.getInstance().getTimeZone().getID(); boolean ignoreFatOrphanFiles = false; - run(deviceId, imagePath.toString(), 0, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callback); + run(deviceId, Paths.get(src.toString(), SPARSE_IMAGE_VHD).toString(), 0, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callback); } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.form index 48248855b0..79e6241551 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.form @@ -1,6 +1,6 @@ -
+ @@ -69,7 +69,7 @@ - + @@ -213,6 +213,9 @@ + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java index 981a4fa800..2654293ca2 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java @@ -172,6 +172,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { } )); imageTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF); + imageTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); imageTable.setShowHorizontalLines(false); imageTable.setShowVerticalLines(false); imageTable.getTableHeader().setReorderingAllowed(false); @@ -227,7 +228,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { .addGroup(layout.createSequentialGroup() .addGap(144, 144, 144) .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 116, javax.swing.GroupLayout.PREFERRED_SIZE)))) - .addContainerGap(68, Short.MAX_VALUE)))) + .addContainerGap(48, Short.MAX_VALUE)))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -468,6 +469,9 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { setNormalMessage(Bundle.LogicalImagerPanel_messageLabel_clickScanOrBrowse()); } + public void popDownPanel() { + } + /** * Should we enable the next button of the wizard? * From a783fdbf327baec3404432b57ec4d50a240a6f2b Mon Sep 17 00:00:00 2001 From: esaunders Date: Tue, 14 May 2019 16:54:39 -0400 Subject: [PATCH 33/57] Yet another fix for goto page text field. --- .../sleuthkit/autopsy/corecomponents/DataResultViewerTable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 4b47ae2faf..a5bbc461b8 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -909,7 +909,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { pageNextButton.setEnabled(currentPage != totalPages); pagePrevButton.setEnabled(currentPage != 1); - gotoPageTextField.setEnabled(currentPage != totalPages); + gotoPageTextField.setEnabled(totalPages > 1); gotoPageTextField.setText(""); } } From a1d8832abb4bf3b2bd54201dd0f3448b4ae6758d Mon Sep 17 00:00:00 2001 From: esaunders Date: Tue, 14 May 2019 17:03:41 -0400 Subject: [PATCH 34/57] Fix for slack file filtering issue. --- .../autopsy/datamodel/DataSourcesKnownAndSlackFilter.java | 2 +- .../sleuthkit/autopsy/datamodel/ViewsKnownAndSlackFilter.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesKnownAndSlackFilter.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesKnownAndSlackFilter.java index 5fbff564b5..98eb9a0d1d 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesKnownAndSlackFilter.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesKnownAndSlackFilter.java @@ -39,7 +39,7 @@ class DataSourcesKnownAndSlackFilter extends KnownAndSlackFil UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> { if (evt.getKey().equals(UserPreferences.HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE)) { filterKnown = UserPreferences.hideKnownFilesInDataSourcesTree(); - } else if (evt.getKey().equals(UserPreferences.HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE)) { + } else if (evt.getKey().equals(UserPreferences.HIDE_SLACK_FILES_IN_DATA_SRCS_TREE)) { filterSlack = UserPreferences.hideSlackFilesInDataSourcesTree(); } }); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ViewsKnownAndSlackFilter.java b/Core/src/org/sleuthkit/autopsy/datamodel/ViewsKnownAndSlackFilter.java index c06ba53bf9..f6b7874122 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ViewsKnownAndSlackFilter.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ViewsKnownAndSlackFilter.java @@ -39,7 +39,7 @@ class ViewsKnownAndSlackFilter extends KnownAndSlackFilterBas UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> { if (evt.getKey().equals(UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE)) { filterKnown = UserPreferences.hideKnownFilesInViewsTree(); - } else if (evt.getKey().equals(UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE)) { + } else if (evt.getKey().equals(UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE)) { filterSlack = UserPreferences.hideSlackFilesInViewsTree(); } }); From f33dc7ffd6a3428caee94182a10fbf8963122ef0 Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 15 May 2019 11:43:52 -0400 Subject: [PATCH 35/57] Fix for artifacts not getting filtered out of data source results. --- .../autopsy/directorytree/DataResultFilterNode.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index bc5ecae9ce..daa184def3 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -232,7 +232,7 @@ public class DataResultFilterNode extends FilterNode { private static class DataResultFilterChildren extends FilterNode.Children { private final ExplorerManager sourceEm; - private boolean filterArtifacts; // display message artifacts in the DataSource subtree + private final boolean filterArtifacts; // display message artifacts in the DataSource subtree /** * the constructor @@ -240,7 +240,8 @@ public class DataResultFilterNode extends FilterNode { private DataResultFilterChildren(Node arg, ExplorerManager sourceEm) { super(arg); - this.filterArtifacts = false; + filterArtifacts = SelectionContext.getSelectionContext(arg).equals(SelectionContext.DATA_SOURCES); + this.sourceEm = sourceEm; } From 865ba9def35c265d316b6f6707db584710136aeb Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Wed, 15 May 2019 13:13:53 -0400 Subject: [PATCH 36/57] Don't popup error dialog, use callback --- .../casemodule/LogicalImagerDSProcessor.java | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java index b45fa1ff54..045d9a067f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java @@ -22,9 +22,10 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; import java.util.Calendar; import java.util.UUID; -import javax.swing.JOptionPane; import javax.swing.JPanel; import org.apache.commons.io.FileUtils; import org.openide.util.NbBundle.Messages; @@ -33,6 +34,8 @@ import org.openide.util.lookup.ServiceProviders; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; /** * A Logical Imager data source processor that implements the DataSourceProcessor service @@ -48,6 +51,7 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { private static final String LOGICAL_IMAGER_DIR = "LogicalImager"; //NON-NLS private static final String SPARSE_IMAGE_VHD = "sparse_image.vhd"; //NON-NLS private final LogicalImagerPanel configPanel; + private AddImageTask addImageTask; /* * Constructs a Logical Imager data source processor that implements the @@ -127,10 +131,13 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { configPanel.storeSettings(); Path imageDirPath = configPanel.getImageDirPath(); + List errorList = new ArrayList<>(); + List emptyDataSources = new ArrayList<>(); if (!imageDirPath.toFile().exists()) { // TODO: Better ways to detect ejected USB drive? String msg = imageDirPath.toString() + " not found.\nUSB drive has been ejected."; - JOptionPane.showMessageDialog(null, msg, "ERROR", JOptionPane.ERROR_MESSAGE); + errorList.add(msg); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); return; } // Create the LogicalImager directory under ModuleDirectory @@ -140,7 +147,8 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { if (!logicalImagerDir.mkdir()) { // create failed String msg = "Fail to create directory " + logicalImagerDir; - JOptionPane.showMessageDialog(null, msg, "ERROR", JOptionPane.ERROR_MESSAGE); + errorList.add(msg); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); return; } } @@ -148,8 +156,8 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { if (dest.exists()) { // directory already exists String msg = "Directory " + dest.toString() + " already exists"; - JOptionPane.showMessageDialog(null, msg, "ERROR", JOptionPane.ERROR_MESSAGE); - configPanel.popDownPanel(); + errorList.add(msg); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); return; } File src = imageDirPath.toFile(); @@ -160,7 +168,22 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { } catch (IOException ex) { // Copy directory failed String msg = "Failed to copy directory " + src.toString() + " to " + dest.toString() ; - JOptionPane.showMessageDialog(null, msg, "ERROR", JOptionPane.ERROR_MESSAGE); + errorList.add(msg); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); + return; + } + + // Add the alert.txt and users.txt into the case report + String status = addReport(Paths.get(dest.toString(), "alert.txt"), "alert.txt for " + imageDirPath.toString()); + if (status != null) { + errorList.add(status); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); + return; + } + status = addReport(Paths.get(dest.toString(), "users.txt"), "users.txt for " + imageDirPath.toString()); + if (status != null) { + errorList.add(status); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); return; } @@ -170,6 +193,19 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { run(deviceId, Paths.get(src.toString(), SPARSE_IMAGE_VHD).toString(), 0, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callback); } + /** + * returns null if success, or exception message + * + */ + private String addReport(Path reportPath, String reportName) { + try { + Case.getCurrentCase().addReport(reportPath.toString(), "LogicalImager", reportName); + return null; + } catch (TskCoreException ex) { + String msg = "Failed to add report " + reportPath.toString() + ". Reason= " + ex.getMessage(); + return msg; + } + } /** * Adds a "Logical Imager" data source to the case database using a background task in * a separate thread and the given settings instead of those provided by the @@ -193,12 +229,15 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { * @param callback Callback to call when processing is done. */ private void run(String deviceId, String imagePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { - AddImageTask addImageTask = new AddImageTask(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null, progressMonitor, callback); + addImageTask = new AddImageTask(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null, progressMonitor, callback); new Thread(addImageTask).start(); } @Override public void cancel() { + if (addImageTask != null) { + addImageTask.cancelTask(); + } } /** From d03ca013bc1a410b4e26549dbdee143c0e150f52 Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Wed, 15 May 2019 15:52:12 -0400 Subject: [PATCH 37/57] AddLogicalImageTask to do time consuming work --- .../casemodule/AddLogicalImageTask.java | 139 ++++++++++++++++++ .../casemodule/LogicalImagerDSProcessor.java | 89 +++++------ 2 files changed, 174 insertions(+), 54 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java new file mode 100644 index 0000000000..6b2f5e2e92 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java @@ -0,0 +1,139 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013-2019 Basis Technology Corp. + * Contact: carrier sleuthkit 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.casemodule; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import org.apache.commons.io.FileUtils; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +public class AddLogicalImageTask extends AddImageTask { + + private final Logger logger = Logger.getLogger(AddLogicalImageTask.class.getName()); + private final static String ALERT_TXT = "alert.txt"; //NON-NLS + private final static String USERS_TXT = "users.txt"; //NON-NLS + private final File src; + private final File dest; + private final DataSourceProcessorCallback callback; + private final DataSourceProcessorProgressMonitor progressMonitor; + + public AddLogicalImageTask(String deviceId, String imagePath, int sectorSize, + String timeZone, boolean ignoreFatOrphanFiles, + String md5, String sha1, String sha256, + ImageWriterSettings imageWriterSettings, + File src, File dest, + DataSourceProcessorProgressMonitor progressMonitor, + DataSourceProcessorCallback callback + ) { + super(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, + md5, sha1, sha256, imageWriterSettings, progressMonitor, callback); + this.src = src; + this.dest = dest; + this.progressMonitor = progressMonitor; + this.callback = callback; + } + + /** + * Copy the src directory to dest. + * Add alert.txt and users.txt to the case report + * Adds the image to the case database. + */ + @Messages({ + "AddLogicalImageTask.copyingImageFromTo=Copying image from {0} to {1}", + "AddLogicalImageTask.doneCopying=Done copying", + "AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1}", + "AddLogicalImageTask.addingToReport=Adding {0} to report", + "AddLogicalImageTask.doneAddingToReport=Done adding {0} to report" + }) + @Override + public void run() { + List errorList = new ArrayList<>(); + List emptyDataSources = new ArrayList<>(); + + try { + progressMonitor.setProgressText(Bundle.AddLogicalImageTask_copyingImageFromTo(src.toString(), dest.toString())); + FileUtils.copyDirectory(src, dest); + progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneCopying()); + } catch (IOException ex) { + // Copy directory failed + String msg = Bundle.AddLogicalImageTask_failedToCopyDirectory(src.toString(), dest.toString()); + logger.log(Level.SEVERE, msg); + errorList.add(msg); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); + return; + } + + // Add the alert.txt and users.txt to the case report + progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingToReport(ALERT_TXT)); + String status = addReport(Paths.get(dest.toString(), ALERT_TXT), ALERT_TXT + " " + src.getName()); + if (status != null) { + errorList.add(status); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); + return; + } + progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingToReport(ALERT_TXT)); + + progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingToReport(USERS_TXT)); + status = addReport(Paths.get(dest.toString(), USERS_TXT), USERS_TXT + " " + src.getName()); + if (status != null) { + errorList.add(status); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); + return; + } + progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingToReport(USERS_TXT)); + + super.run(); + } + + /** + * Add a file specified by the reportPath to the case report. + * + * @param reportPath Path to the report to be added + * @param reportName Name associated the report + * @returns null if success, or exception message if failure + * + */ + @Messages({ + "AddLogicalImageTask.failedToAddReport=Failed to add report {0}. Reason= {1}" + }) + private String addReport(Path reportPath, String reportName) { + if (!reportPath.toFile().exists()) { + return null; // if the reportPath doesn't exist, just ignore it. + } + try { + Case.getCurrentCase().addReport(reportPath.toString(), "LogicalImager", reportName); //NON-NLS + return null; + } catch (TskCoreException ex) { + String msg = Bundle.AddLogicalImageTask_failedToAddReport(reportPath.toString(), ex.getMessage()); + logger.log(Level.SEVERE, msg); + return msg; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java index 045d9a067f..56c1193143 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java @@ -19,23 +19,21 @@ package org.sleuthkit.autopsy.casemodule; import java.io.File; -import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.List; import java.util.Calendar; +import java.util.List; import java.util.UUID; import javax.swing.JPanel; -import org.apache.commons.io.FileUtils; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProviders; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; /** * A Logical Imager data source processor that implements the DataSourceProcessor service @@ -48,10 +46,11 @@ import org.sleuthkit.datamodel.TskCoreException; ) public class LogicalImagerDSProcessor implements DataSourceProcessor { + private final Logger logger = Logger.getLogger(LogicalImagerDSProcessor.class.getName()); private static final String LOGICAL_IMAGER_DIR = "LogicalImager"; //NON-NLS private static final String SPARSE_IMAGE_VHD = "sparse_image.vhd"; //NON-NLS private final LogicalImagerPanel configPanel; - private AddImageTask addImageTask; + private AddLogicalImageTask addLogicalImageTask; /* * Constructs a Logical Imager data source processor that implements the @@ -127,26 +126,36 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { * @param callback Callback that will be used by the background task * to return results. */ + @Messages({ + "LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.", + "LogicalImagerDSProcessor.failToCreateDirectory=Fail to create directory {0}", + "LogicalImagerDSProcessor.directoryAlreadyExists=Directory {0} already exists", + }) @Override public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { configPanel.storeSettings(); + Path imageDirPath = configPanel.getImageDirPath(); List errorList = new ArrayList<>(); List emptyDataSources = new ArrayList<>(); + if (!imageDirPath.toFile().exists()) { + // This can happen if the USB drive was selected in the panel, but + // was ejected before pressing the NEXT button // TODO: Better ways to detect ejected USB drive? - String msg = imageDirPath.toString() + " not found.\nUSB drive has been ejected."; + String msg = Bundle.LogicalImagerDSProcessor_imageDirPathNotFound(imageDirPath.toString()); errorList.add(msg); callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); return; } + // Create the LogicalImager directory under ModuleDirectory String moduleDirectory = Case.getCurrentCase().getModuleDirectory(); File logicalImagerDir = Paths.get(moduleDirectory, LOGICAL_IMAGER_DIR).toFile(); if (!logicalImagerDir.exists()) { if (!logicalImagerDir.mkdir()) { // create failed - String msg = "Fail to create directory " + logicalImagerDir; + String msg = Bundle.LogicalImagerDSProcessor_failToCreateDirectory(logicalImagerDir); errorList.add(msg); callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); return; @@ -154,58 +163,22 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { } File dest = Paths.get(logicalImagerDir.toString(), imageDirPath.getFileName().toString()).toFile(); if (dest.exists()) { - // directory already exists - String msg = "Directory " + dest.toString() + " already exists"; + // Destination directory already exists + String msg = Bundle.LogicalImagerDSProcessor_directoryAlreadyExists(dest.toString()); errorList.add(msg); callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); return; } File src = imageDirPath.toFile(); - try { - configPanel.setMessageLabel("Copying " + src.toString() + " directory to " + dest.toString()); - FileUtils.copyDirectory(src, dest); - configPanel.setMessageLabel(""); - } catch (IOException ex) { - // Copy directory failed - String msg = "Failed to copy directory " + src.toString() + " to " + dest.toString() ; - errorList.add(msg); - callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); - return; - } - - // Add the alert.txt and users.txt into the case report - String status = addReport(Paths.get(dest.toString(), "alert.txt"), "alert.txt for " + imageDirPath.toString()); - if (status != null) { - errorList.add(status); - callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); - return; - } - status = addReport(Paths.get(dest.toString(), "users.txt"), "users.txt for " + imageDirPath.toString()); - if (status != null) { - errorList.add(status); - callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); - return; - } - + String deviceId = UUID.randomUUID().toString(); String timeZone = Calendar.getInstance().getTimeZone().getID(); boolean ignoreFatOrphanFiles = false; - run(deviceId, Paths.get(src.toString(), SPARSE_IMAGE_VHD).toString(), 0, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callback); + run(deviceId, Paths.get(src.toString(), SPARSE_IMAGE_VHD).toString(), 0, + timeZone, ignoreFatOrphanFiles, null, null, null, src, dest, + progressMonitor, callback); } - /** - * returns null if success, or exception message - * - */ - private String addReport(Path reportPath, String reportName) { - try { - Case.getCurrentCase().addReport(reportPath.toString(), "LogicalImager", reportName); - return null; - } catch (TskCoreException ex) { - String msg = "Failed to add report " + reportPath.toString() + ". Reason= " + ex.getMessage(); - return msg; - } - } /** * Adds a "Logical Imager" data source to the case database using a background task in * a separate thread and the given settings instead of those provided by the @@ -224,19 +197,27 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { * @param chunkSize The maximum size of each chunk of the raw * data source as it is divided up into virtual * unallocated space files. + * @param src The source directory of image. + * @param dest The destination directory to copy the source. * @param progressMonitor Progress monitor for reporting progress * during processing. * @param callback Callback to call when processing is done. */ - private void run(String deviceId, String imagePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { - addImageTask = new AddImageTask(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null, progressMonitor, callback); - new Thread(addImageTask).start(); + private void run(String deviceId, String imagePath, int sectorSize, String timeZone, + boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, + File src, File dest, + DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback + ) { + addLogicalImageTask = new AddLogicalImageTask(deviceId, imagePath, sectorSize, + timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null, src, dest, + progressMonitor, callback); + new Thread(addLogicalImageTask).start(); } @Override public void cancel() { - if (addImageTask != null) { - addImageTask.cancelTask(); + if (addLogicalImageTask != null) { + addLogicalImageTask.cancelTask(); } } From 8be12fb38e620223acca5b06313971bc623e4800 Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Wed, 15 May 2019 15:58:20 -0400 Subject: [PATCH 38/57] Fix @Messages warning --- .../autopsy/casemodule/AddLogicalImageTask.java | 10 +++++----- .../autopsy/casemodule/LogicalImagerDSProcessor.java | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java index 6b2f5e2e92..2911ab3ed4 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java @@ -66,11 +66,11 @@ public class AddLogicalImageTask extends AddImageTask { * Adds the image to the case database. */ @Messages({ - "AddLogicalImageTask.copyingImageFromTo=Copying image from {0} to {1}", + "# {0} - src", "# {1} - dest", "AddLogicalImageTask.copyingImageFromTo=Copying image from {0} to {1}", "AddLogicalImageTask.doneCopying=Done copying", - "AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1}", - "AddLogicalImageTask.addingToReport=Adding {0} to report", - "AddLogicalImageTask.doneAddingToReport=Done adding {0} to report" + "# {0} - src", "# {1} - dest", "AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1}", + "# {0} - file", "AddLogicalImageTask.addingToReport=Adding {0} to report", + "# {0} - file", "AddLogicalImageTask.doneAddingToReport=Done adding {0} to report" }) @Override public void run() { @@ -121,7 +121,7 @@ public class AddLogicalImageTask extends AddImageTask { * */ @Messages({ - "AddLogicalImageTask.failedToAddReport=Failed to add report {0}. Reason= {1}" + "# {0} - file", "# {1} - exception message", "AddLogicalImageTask.failedToAddReport=Failed to add report {0}. Reason= {1}" }) private String addReport(Path reportPath, String reportName) { if (!reportPath.toFile().exists()) { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java index 56c1193143..3baf1a954a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java @@ -127,9 +127,9 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { * to return results. */ @Messages({ - "LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.", - "LogicalImagerDSProcessor.failToCreateDirectory=Fail to create directory {0}", - "LogicalImagerDSProcessor.directoryAlreadyExists=Directory {0} already exists", + "# {0} - imageDirPath", "LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.", + "# {0} - directory", "LogicalImagerDSProcessor.failToCreateDirectory=Fail to create directory {0}", + "# {0} - directory", "LogicalImagerDSProcessor.directoryAlreadyExists=Directory {0} already exists", }) @Override public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { From d9b443c9657abea352c3a6c817ebbe9f1050c5dd Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Wed, 15 May 2019 16:11:42 -0400 Subject: [PATCH 39/57] merged bundle --- .../casemodule/Bundle.properties-MERGED | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 4150e5729e..45d89e2a45 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -1,5 +1,19 @@ AddImageWizardIngestConfigPanel.name.text=Configure Ingest Modules AddImageWizardSelectDspVisual.multiUserWarning.text=This type of Data Source Processor is not available in multi-user mode +# {0} - file +AddLogicalImageTask.addingToReport=Adding {0} to report +# {0} - src +# {1} - dest +AddLogicalImageTask.copyingImageFromTo=Copying image from {0} to {1} +# {0} - file +AddLogicalImageTask.doneAddingToReport=Done adding {0} to report +AddLogicalImageTask.doneCopying=Done copying +# {0} - file +# {1} - exception message +AddLogicalImageTask.failedToAddReport=Failed to add report {0}. Reason= {1} +# {0} - src +# {1} - dest +AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1} # {0} - exception message Case.closeException.couldNotCloseCase=Error closing case: {0} Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory @@ -169,6 +183,12 @@ LogicalEvidenceFilePanel.validatePanel.nonL01Error.text=Only files with the .l01 LogicalFilesDspPanel.subTypeComboBox.l01FileOption.text=Logical evidence file (L01) LogicalFilesDspPanel.subTypeComboBox.localFilesOption.text=Local files and folders LogicalImagerDSProcessor.dataSourceType=Autopsy Imager +# {0} - directory +LogicalImagerDSProcessor.directoryAlreadyExists=Directory {0} already exists +# {0} - directory +LogicalImagerDSProcessor.failToCreateDirectory=Fail to create directory {0} +# {0} - imageDirPath +LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected. LogicalImagerPanel.imageTable.columnModel.title0=Hostname LogicalImagerPanel.imageTable.columnModel.title1=Extracted Date LogicalImagerPanel.messageLabel.clickScanOrBrowse=Click SCAN or BROWSE button to find images From f827ba58f74706fbf855a45a479903945abcde29 Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Wed, 15 May 2019 17:00:31 -0400 Subject: [PATCH 40/57] Remove unused field --- .../sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java index 3baf1a954a..5ccb86dfe8 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java @@ -46,7 +46,6 @@ import org.sleuthkit.datamodel.Content; ) public class LogicalImagerDSProcessor implements DataSourceProcessor { - private final Logger logger = Logger.getLogger(LogicalImagerDSProcessor.class.getName()); private static final String LOGICAL_IMAGER_DIR = "LogicalImager"; //NON-NLS private static final String SPARSE_IMAGE_VHD = "sparse_image.vhd"; //NON-NLS private final LogicalImagerPanel configPanel; From cb38b15ff044f1dd43f7bb4610e81b78baf94fb1 Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Wed, 15 May 2019 17:01:12 -0400 Subject: [PATCH 41/57] Fix Codacy error --- .../org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java index 2911ab3ed4..f95ff2264e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java @@ -36,7 +36,7 @@ import org.sleuthkit.datamodel.TskCoreException; public class AddLogicalImageTask extends AddImageTask { - private final Logger logger = Logger.getLogger(AddLogicalImageTask.class.getName()); + private final static Logger logger = Logger.getLogger(AddLogicalImageTask.class.getName()); private final static String ALERT_TXT = "alert.txt"; //NON-NLS private final static String USERS_TXT = "users.txt"; //NON-NLS private final File src; From d9ae4dc8cbfa754834dc811a0fb0dea6f507b3fb Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 16 May 2019 14:08:17 -0400 Subject: [PATCH 42/57] 5093 initial change of Html viewer to use JavaFx --- .../autopsy/contentviewers/HtmlPanel.form | 29 ++----- .../autopsy/contentviewers/HtmlPanel.java | 76 +++++++++---------- 2 files changed, 46 insertions(+), 59 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form index 54e6d45006..b9c5e2ba13 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form @@ -16,41 +16,24 @@ - - + + - - + + - - - - - - - - - - - - - - - - - @@ -61,5 +44,9 @@ + + + + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java index 31731c006f..066a950002 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java @@ -18,6 +18,10 @@ */ package org.sleuthkit.autopsy.contentviewers; +import javafx.application.Platform; +import javafx.scene.web.WebView; +import javafx.embed.swing.JFXPanel; +import javafx.scene.Scene; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.openide.util.NbBundle.Messages; @@ -29,47 +33,46 @@ import org.openide.util.NbBundle.Messages; final class HtmlPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; - + private WebView webView; private String htmlText; - + private JFXPanel jfxPanel = new JFXPanel(); + /** * Creates new form HtmlViewerPanel */ HtmlPanel() { initComponents(); - - Utilities.configureTextPaneAsHtml(htmlbodyTextPane); + //wjs + Platform.runLater(() -> { + webView = new WebView(); + webView.getEngine().setJavaScriptEnabled(false); + Scene scene = new Scene(webView); + jfxPanel.setScene(scene); + htmlScrollPane.setViewportView(jfxPanel); + }); } - + /** * Set the text pane's HTML text and refresh the view to display it. - * + * * @param htmlText The HTML text to be applied to the text pane. */ void setHtmlText(String htmlText) { this.htmlText = htmlText; refresh(); } - + /** * Clear the HTML in the text pane and disable the show/hide button. */ void reset() { - htmlbodyTextPane.setText(""); + //wjs + Platform.runLater(() -> { + webView.getEngine().loadContent(""); + }); showImagesToggleButton.setEnabled(false); } - /** - * Guarantee the HTML text has 'html' and 'body' tags. - * - * @param htmlText The HTML text - * - * @return The HTML text with the 'html' and 'body' tags applied. - */ - private String wrapInHtmlBody(String htmlText) { - return "" + htmlText + ""; - } - /** * Cleans out input HTML string * @@ -86,29 +89,33 @@ final class HtmlPanel extends javax.swing.JPanel { return doc.html(); } - + /** * Refresh the panel to reflect the current show/hide images setting. */ @Messages({ "HtmlPanel_showImagesToggleButton_show=Show Images", "HtmlPanel_showImagesToggleButton_hide=Hide Images", - "Html_text_display_error=The HTML text cannot be displayed, it may not be correctly formed HTML.", - }) + "Html_text_display_error=The HTML text cannot be displayed, it may not be correctly formed HTML.",}) private void refresh() { if (false == htmlText.isEmpty()) { try { if (showImagesToggleButton.isSelected()) { showImagesToggleButton.setText(Bundle.HtmlPanel_showImagesToggleButton_hide()); - this.htmlbodyTextPane.setText(wrapInHtmlBody(htmlText)); + Platform.runLater(() -> { + webView.getEngine().loadContent("JUST A STRING"); + }); } else { showImagesToggleButton.setText(Bundle.HtmlPanel_showImagesToggleButton_show()); - this.htmlbodyTextPane.setText(wrapInHtmlBody(cleanseHTML(htmlText))); + Platform.runLater(() -> { + webView.getEngine().loadContent(cleanseHTML("JUST A STRING")); + }); } showImagesToggleButton.setEnabled(true); - htmlbodyTextPane.setCaretPosition(0); - } catch(Exception ex) { - this.htmlbodyTextPane.setText(wrapInHtmlBody(Bundle.Html_text_display_error())); + } catch (Exception ignored) { + Platform.runLater(() -> { + webView.getEngine().loadContent(Bundle.Html_text_display_error()); + }); } } } @@ -122,14 +129,8 @@ final class HtmlPanel extends javax.swing.JPanel { // //GEN-BEGIN:initComponents private void initComponents() { - htmlScrollPane = new javax.swing.JScrollPane(); - htmlbodyTextPane = new javax.swing.JTextPane(); showImagesToggleButton = new javax.swing.JToggleButton(); - - htmlScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); - - htmlbodyTextPane.setEditable(false); - htmlScrollPane.setViewportView(htmlbodyTextPane); + htmlScrollPane = new javax.swing.JScrollPane(); org.openide.awt.Mnemonics.setLocalizedText(showImagesToggleButton, org.openide.util.NbBundle.getMessage(HtmlPanel.class, "HtmlPanel.showImagesToggleButton.text")); // NOI18N showImagesToggleButton.addActionListener(new java.awt.event.ActionListener() { @@ -142,17 +143,17 @@ final class HtmlPanel extends javax.swing.JPanel { this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(htmlScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addComponent(showImagesToggleButton) - .addGap(0, 0, Short.MAX_VALUE)) + .addGap(0, 203, Short.MAX_VALUE)) + .addComponent(htmlScrollPane) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(showImagesToggleButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(htmlScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 71, Short.MAX_VALUE)) + .addComponent(htmlScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 74, Short.MAX_VALUE)) ); }// //GEN-END:initComponents @@ -163,7 +164,6 @@ final class HtmlPanel extends javax.swing.JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JScrollPane htmlScrollPane; - private javax.swing.JTextPane htmlbodyTextPane; private javax.swing.JToggleButton showImagesToggleButton; // End of variables declaration//GEN-END:variables } From 430c05436d8109ddcbf640a344841430df1f1b9a Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Thu, 16 May 2019 14:41:21 -0400 Subject: [PATCH 43/57] Fix typo --- .../sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java index 5ccb86dfe8..740570c8ee 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java @@ -32,7 +32,6 @@ import org.openide.util.lookup.ServiceProviders; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.Content; /** @@ -127,7 +126,7 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { */ @Messages({ "# {0} - imageDirPath", "LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.", - "# {0} - directory", "LogicalImagerDSProcessor.failToCreateDirectory=Fail to create directory {0}", + "# {0} - directory", "LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0}", "# {0} - directory", "LogicalImagerDSProcessor.directoryAlreadyExists=Directory {0} already exists", }) @Override From 8c2437157c6f07ef71eb557c2efe7c5b8a8094d5 Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Thu, 16 May 2019 14:56:41 -0400 Subject: [PATCH 44/57] log exceptions. Don't limit text sizes --- .../texttranslation/ui/TranslatedTextViewer.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslatedTextViewer.java b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslatedTextViewer.java index 4a57368a0b..e9656bb0a9 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslatedTextViewer.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslatedTextViewer.java @@ -52,6 +52,8 @@ import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException; import org.sleuthkit.autopsy.texttranslation.TranslationException; import org.sleuthkit.datamodel.Content; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.texttranslation.ui.TranslationContentPanel.DisplayDropdownOptions; @@ -61,6 +63,8 @@ import org.sleuthkit.autopsy.texttranslation.ui.TranslationContentPanel.DisplayD @ServiceProvider(service = TextViewer.class, position = 4) public final class TranslatedTextViewer implements TextViewer { + private static final Logger logger = Logger.getLogger(TranslatedTextViewer.class.getName()); + private static final boolean OCR_ENABLED = true; private static final boolean OCR_DISABLED = false; private static final int MAX_SIZE_1MB = 1024000; @@ -169,16 +173,20 @@ public final class TranslatedTextViewer implements TextViewer { try { return getFileText(node); } catch (IOException ex) { + logger.log(Level.WARNING, "Error getting text", ex); return Bundle.TranslatedContentViewer_errorMsg(); } catch (TextExtractor.InitReaderException ex) { + logger.log(Level.WARNING, "Error getting text", ex); return Bundle.TranslatedContentViewer_errorExtractingText(); } } else { try { return translate(getFileText(node)); } catch (IOException ex) { + logger.log(Level.WARNING, "Error translating text", ex); return Bundle.TranslatedContentViewer_errorMsg(); } catch (TextExtractor.InitReaderException ex) { + logger.log(Level.WARNING, "Error translating text", ex); return Bundle.TranslatedContentViewer_errorExtractingText(); } } @@ -247,7 +255,8 @@ public final class TranslatedTextViewer implements TextViewer { } catch (NoServiceProviderException ex) { return Bundle.TranslatedContentViewer_noServiceProvider(); } catch (TranslationException ex) { - return Bundle.TranslatedContentViewer_translationException(); + logger.log(Level.WARNING, "Error translating text", ex); + return Bundle.TranslatedContentViewer_translationException() + " (" + ex.getMessage() + ")"; } } @@ -287,7 +296,7 @@ public final class TranslatedTextViewer implements TextViewer { //Correct for UTF-8 byte[] resultInUTF8Bytes = result.getBytes("UTF8"); - byte[] trimTo1MB = Arrays.copyOfRange(resultInUTF8Bytes, 0, MAX_SIZE_1MB / 1000); + byte[] trimTo1MB = Arrays.copyOfRange(resultInUTF8Bytes, 0, MAX_SIZE_1MB ); return new String(trimTo1MB, "UTF-8"); } @@ -333,7 +342,8 @@ public final class TranslatedTextViewer implements TextViewer { bytesRead += read; } - return textBuilder.toString(); + // The trim is on here because HTML files were observed with nearly 1MB of white space at the end + return textBuilder.toString().trim(); } /** From 9c602bf1c5a2f59e627c8b788bdb47006db47f81 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 16 May 2019 15:26:59 -0400 Subject: [PATCH 45/57] 5093 fix scroll bar for JavaFX html viewer --- .../autopsy/contentviewers/HtmlPanel.form | 10 ++++---- .../autopsy/contentviewers/HtmlPanel.java | 23 +++++++++++-------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form index b9c5e2ba13..072e4ceece 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form @@ -20,15 +20,15 @@ - + - - + + @@ -44,9 +44,9 @@ - + - + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java index 066a950002..b61c6a450b 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java @@ -33,8 +33,10 @@ import org.openide.util.NbBundle.Messages; final class HtmlPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; + private static final String TEXT_TYPE="text/plain"; private WebView webView; private String htmlText; + private JFXPanel jfxPanel = new JFXPanel(); /** @@ -48,7 +50,8 @@ final class HtmlPanel extends javax.swing.JPanel { webView.getEngine().setJavaScriptEnabled(false); Scene scene = new Scene(webView); jfxPanel.setScene(scene); - htmlScrollPane.setViewportView(jfxPanel); + jfxPanel.setPreferredSize(htmlJPanel.getPreferredSize()); + htmlJPanel.add(jfxPanel); }); } @@ -68,7 +71,7 @@ final class HtmlPanel extends javax.swing.JPanel { void reset() { //wjs Platform.runLater(() -> { - webView.getEngine().loadContent(""); + webView.getEngine().loadContent("", TEXT_TYPE); }); showImagesToggleButton.setEnabled(false); } @@ -103,18 +106,18 @@ final class HtmlPanel extends javax.swing.JPanel { if (showImagesToggleButton.isSelected()) { showImagesToggleButton.setText(Bundle.HtmlPanel_showImagesToggleButton_hide()); Platform.runLater(() -> { - webView.getEngine().loadContent("JUST A STRING"); + webView.getEngine().loadContent(htmlText); }); } else { showImagesToggleButton.setText(Bundle.HtmlPanel_showImagesToggleButton_show()); Platform.runLater(() -> { - webView.getEngine().loadContent(cleanseHTML("JUST A STRING")); + webView.getEngine().loadContent(cleanseHTML(htmlText)); }); } showImagesToggleButton.setEnabled(true); } catch (Exception ignored) { Platform.runLater(() -> { - webView.getEngine().loadContent(Bundle.Html_text_display_error()); + webView.getEngine().loadContent(Bundle.Html_text_display_error(), TEXT_TYPE); }); } } @@ -130,7 +133,7 @@ final class HtmlPanel extends javax.swing.JPanel { private void initComponents() { showImagesToggleButton = new javax.swing.JToggleButton(); - htmlScrollPane = new javax.swing.JScrollPane(); + htmlJPanel = new javax.swing.JPanel(); org.openide.awt.Mnemonics.setLocalizedText(showImagesToggleButton, org.openide.util.NbBundle.getMessage(HtmlPanel.class, "HtmlPanel.showImagesToggleButton.text")); // NOI18N showImagesToggleButton.addActionListener(new java.awt.event.ActionListener() { @@ -139,6 +142,8 @@ final class HtmlPanel extends javax.swing.JPanel { } }); + htmlJPanel.setLayout(new java.awt.BorderLayout()); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -146,14 +151,14 @@ final class HtmlPanel extends javax.swing.JPanel { .addGroup(layout.createSequentialGroup() .addComponent(showImagesToggleButton) .addGap(0, 203, Short.MAX_VALUE)) - .addComponent(htmlScrollPane) + .addComponent(htmlJPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(showImagesToggleButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(htmlScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 74, Short.MAX_VALUE)) + .addComponent(htmlJPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 56, Short.MAX_VALUE)) ); }// //GEN-END:initComponents @@ -163,7 +168,7 @@ final class HtmlPanel extends javax.swing.JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JScrollPane htmlScrollPane; + private javax.swing.JPanel htmlJPanel; private javax.swing.JToggleButton showImagesToggleButton; // End of variables declaration//GEN-END:variables } From 5cf30a99dcd1f5f874b832584924ea145faaa021 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 16 May 2019 15:33:08 -0400 Subject: [PATCH 46/57] 5093 formatting and sizing changes --- .../sleuthkit/autopsy/contentviewers/HtmlPanel.form | 4 ++-- .../sleuthkit/autopsy/contentviewers/HtmlPanel.java | 13 +++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form index 072e4ceece..97b3c67f72 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form @@ -18,7 +18,7 @@ - + @@ -28,7 +28,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java index b61c6a450b..a034978179 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java @@ -33,18 +33,16 @@ import org.openide.util.NbBundle.Messages; final class HtmlPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; - private static final String TEXT_TYPE="text/plain"; + private static final String TEXT_TYPE = "text/plain"; + private final JFXPanel jfxPanel = new JFXPanel(); private WebView webView; private String htmlText; - - private JFXPanel jfxPanel = new JFXPanel(); /** * Creates new form HtmlViewerPanel */ HtmlPanel() { initComponents(); - //wjs Platform.runLater(() -> { webView = new WebView(); webView.getEngine().setJavaScriptEnabled(false); @@ -69,7 +67,6 @@ final class HtmlPanel extends javax.swing.JPanel { * Clear the HTML in the text pane and disable the show/hide button. */ void reset() { - //wjs Platform.runLater(() -> { webView.getEngine().loadContent("", TEXT_TYPE); }); @@ -84,7 +81,7 @@ final class HtmlPanel extends javax.swing.JPanel { * @return The cleansed HTML String */ private String cleanseHTML(String htmlInString) { - + //this will not remove all images just the ones in the image tags matching this format Document doc = Jsoup.parse(htmlInString); // Update all 'img' tags. @@ -150,7 +147,7 @@ final class HtmlPanel extends javax.swing.JPanel { layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(showImagesToggleButton) - .addGap(0, 203, Short.MAX_VALUE)) + .addGap(0, 95, Short.MAX_VALUE)) .addComponent(htmlJPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( @@ -158,7 +155,7 @@ final class HtmlPanel extends javax.swing.JPanel { .addGroup(layout.createSequentialGroup() .addComponent(showImagesToggleButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(htmlJPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 56, Short.MAX_VALUE)) + .addComponent(htmlJPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 33, Short.MAX_VALUE)) ); }// //GEN-END:initComponents From 4c5ef90e480ee9d4dd8a185be7f4305d37f3814b Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Thu, 16 May 2019 16:04:28 -0400 Subject: [PATCH 47/57] don't force newlines in HTML extraction. This was causing problems with NLP use cases --- .../org/sleuthkit/autopsy/textextractors/HtmlTextExtractor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/HtmlTextExtractor.java b/Core/src/org/sleuthkit/autopsy/textextractors/HtmlTextExtractor.java index d117e80537..80218d039f 100644 --- a/Core/src/org/sleuthkit/autopsy/textextractors/HtmlTextExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/textextractors/HtmlTextExtractor.java @@ -198,6 +198,7 @@ final class HtmlTextExtractor implements TextExtractor { renderer.setIncludeHyperlinkURLs(false); renderer.setDecorateFontStyles(false); renderer.setIncludeAlternateText(false); + renderer.setMaxLineLength(0); // don't force wrapping return new StringReader(renderer.toString()); } catch (IOException ex) { logger.log(Level.WARNING, "Error extracting HTML from content.", ex); From 996683333d46ff678a9cd8cebb10eca94e1af1cd Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Thu, 16 May 2019 17:49:35 -0400 Subject: [PATCH 48/57] Dont use Bundle for logger messages. --- .../sleuthkit/autopsy/casemodule/AddLogicalImageTask.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java index f95ff2264e..fd80d9afe9 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java @@ -68,7 +68,6 @@ public class AddLogicalImageTask extends AddImageTask { @Messages({ "# {0} - src", "# {1} - dest", "AddLogicalImageTask.copyingImageFromTo=Copying image from {0} to {1}", "AddLogicalImageTask.doneCopying=Done copying", - "# {0} - src", "# {1} - dest", "AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1}", "# {0} - file", "AddLogicalImageTask.addingToReport=Adding {0} to report", "# {0} - file", "AddLogicalImageTask.doneAddingToReport=Done adding {0} to report" }) @@ -83,7 +82,7 @@ public class AddLogicalImageTask extends AddImageTask { progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneCopying()); } catch (IOException ex) { // Copy directory failed - String msg = Bundle.AddLogicalImageTask_failedToCopyDirectory(src.toString(), dest.toString()); + String msg = String.format("Failed to copy directory {0} to {1}", src.toString(), dest.toString()); logger.log(Level.SEVERE, msg); errorList.add(msg); callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); @@ -120,9 +119,6 @@ public class AddLogicalImageTask extends AddImageTask { * @returns null if success, or exception message if failure * */ - @Messages({ - "# {0} - file", "# {1} - exception message", "AddLogicalImageTask.failedToAddReport=Failed to add report {0}. Reason= {1}" - }) private String addReport(Path reportPath, String reportName) { if (!reportPath.toFile().exists()) { return null; // if the reportPath doesn't exist, just ignore it. @@ -131,7 +127,7 @@ public class AddLogicalImageTask extends AddImageTask { Case.getCurrentCase().addReport(reportPath.toString(), "LogicalImager", reportName); //NON-NLS return null; } catch (TskCoreException ex) { - String msg = Bundle.AddLogicalImageTask_failedToAddReport(reportPath.toString(), ex.getMessage()); + String msg = String.format("Failed to add report {0}. Reason= {1}", reportPath.toString(), ex.getMessage()); logger.log(Level.SEVERE, msg); return msg; } From 93c2a7411d8dcf8984eefbc357d64165b9ef5f82 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 16 May 2019 18:11:51 -0400 Subject: [PATCH 49/57] 5093 disable linking, right click menu, and fix image disabling --- .../autopsy/contentviewers/HtmlPanel.java | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java index a034978179..10471378c0 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java @@ -19,12 +19,18 @@ package org.sleuthkit.autopsy.contentviewers; import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.concurrent.Worker; import javafx.scene.web.WebView; import javafx.embed.swing.JFXPanel; import javafx.scene.Scene; import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; +import org.jsoup.nodes.Node; import org.openide.util.NbBundle.Messages; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.w3c.dom.events.EventTarget; /** * A file content viewer for HTML files. @@ -45,7 +51,19 @@ final class HtmlPanel extends javax.swing.JPanel { initComponents(); Platform.runLater(() -> { webView = new WebView(); + //disable the context menu so they can't open linked pages by right clicking + webView.setContextMenuEnabled(false); + //disable java script webView.getEngine().setJavaScriptEnabled(false); + //disable clicking on links + webView.getEngine().getLoadWorker().stateProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Worker.State oldValue, Worker.State newValue) { + if (newValue == Worker.State.SUCCEEDED) { + disableHyperLinks(); + } + } + }); Scene scene = new Scene(webView); jfxPanel.setScene(scene); jfxPanel.setPreferredSize(htmlJPanel.getPreferredSize()); @@ -81,12 +99,9 @@ final class HtmlPanel extends javax.swing.JPanel { * @return The cleansed HTML String */ private String cleanseHTML(String htmlInString) { - //this will not remove all images just the ones in the image tags matching this format - Document doc = Jsoup.parse(htmlInString); - - // Update all 'img' tags. - doc.select("img[src]").forEach(img -> img.attr("src", "")); - + org.jsoup.nodes.Document doc = Jsoup.parse(htmlInString); + // remove all 'img' tags. + doc.select("img").stream().forEach(Node::remove); return doc.html(); } @@ -163,6 +178,24 @@ final class HtmlPanel extends javax.swing.JPanel { refresh(); }//GEN-LAST:event_showImagesToggleButtonActionPerformed + /** + * Disable the click events on hyper links so that new pages can not be + * opened. + */ + private void disableHyperLinks() { + Platform.runLater(() -> { + Document document = webView.getEngine().getDocument(); + if (document != null) { + NodeList nodeList = document.getElementsByTagName("a"); + for (int i = 0; i < nodeList.getLength(); i++) { + ((EventTarget) nodeList.item(i)).addEventListener("click", (evt) -> { + evt.preventDefault(); + evt.stopPropagation(); + }, true); + } + } + }); + } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel htmlJPanel; From 3af71afd9674066e2857be3f9504037fd2280476 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 16 May 2019 18:22:43 -0400 Subject: [PATCH 50/57] 5093 disable displaying of span tags, usually ads with images --- Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java index 10471378c0..f915b8d5a6 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java @@ -102,6 +102,8 @@ final class HtmlPanel extends javax.swing.JPanel { org.jsoup.nodes.Document doc = Jsoup.parse(htmlInString); // remove all 'img' tags. doc.select("img").stream().forEach(Node::remove); + // remove all 'span' tags, these are often images which are ads + doc.select("span").stream().forEach(Node::remove); return doc.html(); } From 1df0a4f60c46fdf88eb8f7796076b23febe8d3f5 Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Thu, 16 May 2019 19:29:36 -0400 Subject: [PATCH 51/57] Fix --- .../autopsy/casemodule/AddLogicalImageTask.java | 11 +++++++---- .../autopsy/casemodule/Bundle.properties-MERGED | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java index fd80d9afe9..c51755d59d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java @@ -68,6 +68,7 @@ public class AddLogicalImageTask extends AddImageTask { @Messages({ "# {0} - src", "# {1} - dest", "AddLogicalImageTask.copyingImageFromTo=Copying image from {0} to {1}", "AddLogicalImageTask.doneCopying=Done copying", + "# {0} - src", "# {1} - dest", "AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1}", "# {0} - file", "AddLogicalImageTask.addingToReport=Adding {0} to report", "# {0} - file", "AddLogicalImageTask.doneAddingToReport=Done adding {0} to report" }) @@ -82,8 +83,7 @@ public class AddLogicalImageTask extends AddImageTask { progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneCopying()); } catch (IOException ex) { // Copy directory failed - String msg = String.format("Failed to copy directory {0} to {1}", src.toString(), dest.toString()); - logger.log(Level.SEVERE, msg); + String msg = Bundle.AddLogicalImageTask_failedToCopyDirectory(src.toString(), dest.toString()); errorList.add(msg); callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); return; @@ -119,6 +119,9 @@ public class AddLogicalImageTask extends AddImageTask { * @returns null if success, or exception message if failure * */ + @Messages({ + "# {0} - file", "# {1} - exception message", "AddLogicalImageTask.failedToAddReport=Failed to add report {0}. Reason= {1}" + }) private String addReport(Path reportPath, String reportName) { if (!reportPath.toFile().exists()) { return null; // if the reportPath doesn't exist, just ignore it. @@ -127,8 +130,8 @@ public class AddLogicalImageTask extends AddImageTask { Case.getCurrentCase().addReport(reportPath.toString(), "LogicalImager", reportName); //NON-NLS return null; } catch (TskCoreException ex) { - String msg = String.format("Failed to add report {0}. Reason= {1}", reportPath.toString(), ex.getMessage()); - logger.log(Level.SEVERE, msg); + String msg = Bundle.AddLogicalImageTask_failedToAddReport(reportPath.toString(), ex.getMessage()); + logger.log(Level.SEVERE, String.format("Failed to add report {0}. Reason= {1}", reportPath.toString(), ex.getMessage())); return msg; } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 45d89e2a45..8b513280d3 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -186,7 +186,7 @@ LogicalImagerDSProcessor.dataSourceType=Autopsy Imager # {0} - directory LogicalImagerDSProcessor.directoryAlreadyExists=Directory {0} already exists # {0} - directory -LogicalImagerDSProcessor.failToCreateDirectory=Fail to create directory {0} +LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0} # {0} - imageDirPath LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected. LogicalImagerPanel.imageTable.columnModel.title0=Hostname From 1c16f6201896015915cc45c00be7e2ea64d4e443 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 16 May 2019 19:39:53 -0400 Subject: [PATCH 52/57] Add undocumented format parameters --- .../casemodule/LogicalImagerPanel.java | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java index 44d50d0be5..da78e1cfe1 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java @@ -42,15 +42,14 @@ import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; /** - * Panel for adding an logical image file from drive letters. Allows the user - * to select a file. + * Panel for adding an logical image file from drive letters. Allows the user to + * select a file. */ @Messages({ "LogicalImagerPanel.messageLabel.selectedImage=Selected folder", "LogicalImagerPanel.messageLabel.noImageSelected=No image selected", "LogicalImagerPanel.messageLabel.driveHasNoImages=Drive has no images", - "LogicalImagerPanel.selectAcquisitionFromDriveLabel.text=Select acquisition from Drive", -}) + "LogicalImagerPanel.selectAcquisitionFromDriveLabel.text=Select acquisition from Drive",}) @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class LogicalImagerPanel extends JPanel implements DocumentListener { @@ -66,12 +65,12 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { private final String contextName; private Path choosenImageDirPath; private TableModel imageTableModel; - + /** * Creates new form LogicalImagerPanel * - * @param context A string context name used to read/store last - * used settings. + * @param context A string context name used to read/store last used + * settings. */ private LogicalImagerPanel(String context) { this.contextName = context; @@ -82,9 +81,9 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { /** * Creates and returns an instance of a LogicalImagerPanel. * - * @param context A string context name used to read/store last - * used settings. - * + * @param context A string context name used to read/store last used + * settings. + * * @return instance of the LogicalImagerPanel */ @Messages({ @@ -262,12 +261,12 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { int unit = si ? 1000 : 1024; if (bytes < unit) { return bytes + " B"; //NON-NLS - } + } int exp = (int) (Math.log(bytes) / Math.log(unit)); - String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i"); //NON-NLS + String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i"); //NON-NLS return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); //NON-NLS } - + @Messages({ "LogicalImagerPanel.messageLabel.scanningExternalDrives=Scanning external drives for sparse_image.vhd ...", "LogicalImagerPanel.messageLabel.noExternalDriveFound=No drive found" @@ -310,8 +309,11 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { }//GEN-LAST:event_scanButtonActionPerformed @Messages({ + "# {0} - sparseImageDirectory", + "# {1} - image", "LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain {1}", - "LogicalImagerPanel.messageLabel.directoryFormatInvalid=Directory {0} does not match format Logical_Imager_HOSTNAME_yyyymmdd_HH_MM_SS" + "# {0} - invalidFormatDirectory", + "LogicalImagerPanel.messageLabel.directoryFormatInvalid=Directory {0} does not match format Logical_Imager_HOSTNAME_yyyymmdd_HH_MM_SS" }) private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed imageTable.clearSelection(); @@ -325,13 +327,13 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { if (m.find()) { Path vhdPath = Paths.get(path, SPARSE_IMAGE_VHD); if (!vhdPath.toFile().exists()) { - setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_directoryDoesNotContainSparseImage(path,SPARSE_IMAGE_VHD)); + setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_directoryDoesNotContainSparseImage(path, SPARSE_IMAGE_VHD)); firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); return; } choosenImageDirPath = Paths.get(path); setNormalMessage(SELECTED_IMAGE + " " + path); - firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); } else { setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_directoryFormatInvalid(path)); firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); @@ -351,7 +353,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { choosenImageDirPath = null; setErrorMessage(NO_IMAGE_SELECTED); firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); - } + } } private void imageTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_imageTableMouseClicked @@ -372,9 +374,9 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { int row = 0; // Find all directories with name like Logical_Imager_HOSTNAME_yyyymmdd_HH_MM_SS // and has a sparse_image.vhd file in it - for (File file : fList) { - if (file.isDirectory() - && Paths.get(driveLetter, file.getName(), SPARSE_IMAGE_VHD).toFile().exists()) { + for (File file : fList) { + if (file.isDirectory() + && Paths.get(driveLetter, file.getName(), SPARSE_IMAGE_VHD).toFile().exists()) { String dir = file.getName(); Matcher m = regex.matcher(dir); if (m.find()) { @@ -386,7 +388,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { String hour = m.group(5); String minute = m.group(6); String second = m.group(7); - String extractDate = year + "/" + month + "/" + day + String extractDate = year + "/" + month + "/" + day + " " + hour + ":" + minute + ":" + second; imageTableModel.setValueAt(hostname, row, 0); imageTableModel.setValueAt(extractDate, row, 1); @@ -408,31 +410,31 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { choosenImageDirPath = null; setErrorMessage(DRIVE_HAS_NO_IMAGES); } - } + } } - + private void fixImageTableColumnWidth() { int width = imageScrollPane.getPreferredSize().width - 2; imageTable.getColumnModel().getColumn(0).setPreferredWidth((int) (.60 * width)); imageTable.getColumnModel().getColumn(1).setPreferredWidth((int) (.40 * width)); } - + private void setErrorMessage(String msg) { messageLabel.setForeground(Color.red); - messageLabel.setText(msg); + messageLabel.setText(msg); } - + private void setNormalMessage(String msg) { messageLabel.setForeground(Color.black); - messageLabel.setText(msg); + messageLabel.setText(msg); } private void clearImageTable() { imageTableModel = new ImageTableModel(); - imageTable.setModel(imageTableModel); + imageTable.setModel(imageTableModel); fixImageTableColumnWidth(); } - + private void driveListMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_driveListMouseClicked driveListSelect(); firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); @@ -480,7 +482,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { public boolean validatePanel() { return choosenImageDirPath != null && choosenImageDirPath.toFile().exists(); } - + Path getImageDirPath() { return choosenImageDirPath; } @@ -501,6 +503,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { } private class ImageTableModel extends AbstractTableModel { + private final List hostnames = new ArrayList<>(); private final List extractDates = new ArrayList<>(); private final List imageDirPaths = new ArrayList<>(); @@ -534,7 +537,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { } return colName; } - + @Override public Object getValueAt(int rowIndex, int columnIndex) { Object ret = null; @@ -553,7 +556,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { } return ret; } - + @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return false; From 4d65c8cf258c04cce6d4be452716d500ea184256 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 16 May 2019 19:51:37 -0400 Subject: [PATCH 53/57] Commit modified bundle properties files --- .../org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED | 3 +++ .../sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED | 3 +++ .../org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED | 2 ++ 3 files changed, 8 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 4150e5729e..4ec3165e29 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -172,7 +172,10 @@ LogicalImagerDSProcessor.dataSourceType=Autopsy Imager LogicalImagerPanel.imageTable.columnModel.title0=Hostname LogicalImagerPanel.imageTable.columnModel.title1=Extracted Date LogicalImagerPanel.messageLabel.clickScanOrBrowse=Click SCAN or BROWSE button to find images +# {0} - sparseImageDirectory +# {1} - image LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain {1} +# {0} - invalidFormatDirectory LogicalImagerPanel.messageLabel.directoryFormatInvalid=Directory {0} does not match format Logical_Imager_HOSTNAME_yyyymmdd_HH_MM_SS LogicalImagerPanel.messageLabel.driveHasNoImages=Drive has no images LogicalImagerPanel.messageLabel.noExternalDriveFound=No drive found diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED index d2bfb3dddc..2c0af06f50 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED @@ -36,6 +36,9 @@ DataResultViewerTable.firstColLbl=Name DataResultViewerTable.goToPageTextField.err=Invalid page number # {0} - totalPages DataResultViewerTable.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0} +# {0} - currentPage +# {1} - totalPages +DataResultViewerTable.pageNumbers.curOfTotal={0} of {1} DataResultViewerTable.scoreRender.name=S DataResultViewerTable.scoreRender.toolTip=S(core) indicates whether the item is interesting or notable DataResultViewerTable.title=Table diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED index c11a681494..1d8fb7eb14 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED @@ -43,6 +43,8 @@ ArtifactStringContent.attrsTableHeader.type=Type ArtifactStringContent.attrsTableHeader.value=Value ArtifactStringContent.failedToGetAttributes.message=Failed to get some or all attributes from case database ArtifactStringContent.failedToGetSourcePath.message=Failed to get source file path from case database +# {0} - node name +BaseChildFactory.NoSuchEventBusException.message=No event bus for node: {0} BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details BlackboardArtifactNode.createSheet.artifactDetails.name=Result Details BlackboardArtifactNode.createSheet.artifactMD5.displayName=MD5 Hash From af6150c4108d249c6e5a940d28eb90370cb065db Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Fri, 17 May 2019 12:43:55 -0400 Subject: [PATCH 54/57] Log all exceptions --- .../org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java index c51755d59d..4812cddfc1 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java @@ -85,6 +85,7 @@ public class AddLogicalImageTask extends AddImageTask { // Copy directory failed String msg = Bundle.AddLogicalImageTask_failedToCopyDirectory(src.toString(), dest.toString()); errorList.add(msg); + logger.log(Level.SEVERE, String.format("Failed to copy directory {0} to {1}", src.toString(), dest.toString())); callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); return; } From 691c75090bd26bd13753b47c5f3584ca68042826 Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Fri, 17 May 2019 15:37:20 -0400 Subject: [PATCH 55/57] Fix Codacy issues --- .../autopsy/casemodule/AddLogicalImageTask.java | 6 ++++++ .../casemodule/LogicalImagerDSProcessor.java | 14 ++++++-------- .../autopsy/casemodule/LogicalImagerPanel.java | 3 --- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java index 4812cddfc1..d78015816f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java @@ -34,6 +34,12 @@ import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; +/* + * A runnable that + * - copy the logical image folder to a destinction folder + * - add alert.txt and users.txt files to report + * - add an image data source to the case database. + */ public class AddLogicalImageTask extends AddImageTask { private final static Logger logger = Logger.getLogger(AddLogicalImageTask.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java index 740570c8ee..ff01e1c540 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java @@ -150,14 +150,12 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { // Create the LogicalImager directory under ModuleDirectory String moduleDirectory = Case.getCurrentCase().getModuleDirectory(); File logicalImagerDir = Paths.get(moduleDirectory, LOGICAL_IMAGER_DIR).toFile(); - if (!logicalImagerDir.exists()) { - if (!logicalImagerDir.mkdir()) { - // create failed - String msg = Bundle.LogicalImagerDSProcessor_failToCreateDirectory(logicalImagerDir); - errorList.add(msg); - callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); - return; - } + if (!logicalImagerDir.exists() && !logicalImagerDir.mkdir()) { + // create failed + String msg = Bundle.LogicalImagerDSProcessor_failToCreateDirectory(logicalImagerDir); + errorList.add(msg); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); + return; } File dest = Paths.get(logicalImagerDir.toString(), imageDirPath.getFileName().toString()).toFile(); if (dest.exists()) { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java index ec7e1038b6..5d62a1bf11 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java @@ -473,9 +473,6 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { setNormalMessage(Bundle.LogicalImagerPanel_messageLabel_clickScanOrBrowse()); } - public void popDownPanel() { - } - /** * Should we enable the next button of the wizard? * From ac8e7714ea2ff2b88e07108a50f4af47680f8724 Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Fri, 17 May 2019 16:29:48 -0400 Subject: [PATCH 56/57] Fix typo --- .../org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java index d78015816f..e44af9800b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java @@ -36,7 +36,7 @@ import org.sleuthkit.datamodel.TskCoreException; /* * A runnable that - * - copy the logical image folder to a destinction folder + * - copy the logical image folder to a destination folder * - add alert.txt and users.txt files to report * - add an image data source to the case database. */ From ee0fb54dc6499f0c0a7a8c28551be4aedaa24706 Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Fri, 17 May 2019 16:36:13 -0400 Subject: [PATCH 57/57] Fix codacy issue --- .../org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java index e44af9800b..e4d2b89b17 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java @@ -34,7 +34,7 @@ import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; -/* +/** * A runnable that * - copy the logical image folder to a destination folder * - add alert.txt and users.txt files to report