From 638750c7201d860a7136919b70972f1267b67a97 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 23 May 2019 11:46:47 -0400 Subject: [PATCH 001/106] Checkin of the current state of the UI, will continue to make improvements --- .../contentviewers/MediaViewImagePanel.form | 55 ++++ .../contentviewers/MediaViewImagePanel.java | 194 ++++++-------- .../imagetagging/ControlType.java | 32 +++ .../imagetagging/FocusChangeEvent.java | 49 ++++ .../imagetagging/FocusChangeListener.java | 18 ++ .../imagetagging/ImageTagCreator.java | 145 +++++++++++ .../imagetagging/StoredTag.java | 239 ++++++++++++++++++ .../imagetagging/StoredTagEvent.java | 40 +++ .../imagetagging/StoredTagListener.java | 33 +++ .../imagetagging/TopLevelTagsGroup.java | 98 +++++++ 10 files changed, 788 insertions(+), 115 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ControlType.java create mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeEvent.java create mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeListener.java create mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java create mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTag.java create mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagEvent.java create mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagListener.java create mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/TopLevelTagsGroup.java diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form index e057337e6f..fb17c441db 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form @@ -1,6 +1,31 @@
+ + + + + + + + + + + + + + + + + + + + + + + + + @@ -200,6 +225,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index 5c16dbed93..ebf7f48444 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -18,6 +18,11 @@ */ package org.sleuthkit.autopsy.contentviewers; +import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagCreator; +import org.sleuthkit.autopsy.contentviewers.imagetagging.StoredTag; +import org.sleuthkit.autopsy.contentviewers.imagetagging.StoredTagListener; +import org.sleuthkit.autopsy.contentviewers.imagetagging.TopLevelTagsGroup; +import org.sleuthkit.autopsy.contentviewers.imagetagging.StoredTagEvent; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.util.Collections; @@ -32,6 +37,7 @@ import javafx.embed.swing.JFXPanel; import javafx.geometry.Pos; import javafx.geometry.Rectangle2D; import javafx.scene.Cursor; +import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; @@ -49,11 +55,8 @@ import javax.swing.JPanel; import org.controlsfx.control.MaskerPane; import org.openide.util.NbBundle; import org.python.google.common.collect.Lists; -import javafx.scene.Group; -import javafx.scene.input.MouseEvent; -import javafx.scene.paint.Color; -import javafx.scene.shape.Rectangle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.contentviewers.imagetagging.ControlType; import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; @@ -74,9 +77,10 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private final boolean fxInited; private JFXPanel fxPanel; - private Group imageGroup; - private ImageTaggingTool tagger; + private TopLevelTagsGroup imageGroup; + private ImageTagCreator tagger; private ImageView fxImageView; + private Node focusedNode; private ScrollPane scrollPane; private final ProgressBar progressBar = new ProgressBar(); private final MaskerPane maskerPane = new MaskerPane(); @@ -121,8 +125,18 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan // build jfx ui (we could do this in FXML?) fxImageView = new ImageView(); // will hold image - imageGroup = new Group(); - imageGroup.getChildren().add(fxImageView); + imageGroup = new TopLevelTagsGroup(fxImageView); + deleteTagButton.setEnabled(false); + imageGroup.addFocusChangeListener((event) -> { + if(event.getType() == ControlType.NOT_FOCUSED || event.getNode() == fxImageView) { + deleteTagButton.setEnabled(false); + createTagButton.setEnabled(true); + } else if (event.getType() == ControlType.FOCUSED) { + deleteTagButton.setEnabled(true); + createTagButton.setEnabled(false); + focusedNode = event.getNode(); + } + }); scrollPane = new ScrollPane(imageGroup); // scrolls and sizes imageview scrollPane.getStyleClass().add("bg"); //NOI18N scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED); @@ -154,8 +168,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan Platform.runLater(() -> { fxImageView.setViewport(new Rectangle2D(0, 0, 0, 0)); fxImageView.setImage(null); - tagger.defaultSettings(); - + imageGroup.getChildren().clear(); scrollPane.setContent(null); scrollPane.setContent(imageGroup); }); @@ -205,11 +218,17 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan try { Image fxImage = readImageTask.get(); + imageGroup.getChildren().clear(); + imageGroup.getChildren().add(fxImageView); if (nonNull(fxImage)) { // We have a non-null image, so let's show it. fxImageView.setImage(fxImage); - imageGroup.getChildren().remove(tagger); - tagger = new ImageTaggingTool(fxImageView, Color.RED); + tagger = new ImageTagCreator(fxImageView); + StoredTagListener newTagListener = (StoredTagEvent evt) -> { + StoredTag tag = evt.getTag(); + imageGroup.getChildren().add(tag); + }; + tagger.addNewTagListener(newTagListener); imageGroup.getChildren().add(tagger); resetView(); scrollPane.setContent(imageGroup); @@ -293,6 +312,9 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan // //GEN-BEGIN:initComponents private void initComponents() { + jMenu1 = new javax.swing.JMenu(); + jPopupMenu1 = new javax.swing.JPopupMenu(); + jPopupMenu2 = new javax.swing.JPopupMenu(); toolbar = new javax.swing.JToolBar(); rotationTextField = new javax.swing.JTextField(); rotateLeftButton = new javax.swing.JButton(); @@ -303,6 +325,12 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan zoomInButton = new javax.swing.JButton(); jSeparator2 = new javax.swing.JToolBar.Separator(); zoomResetButton = new javax.swing.JButton(); + jSeparator3 = new javax.swing.JToolBar.Separator(); + createTagButton = new javax.swing.JButton(); + jSeparator4 = new javax.swing.JToolBar.Separator(); + deleteTagButton = new javax.swing.JButton(); + + org.openide.awt.Mnemonics.setLocalizedText(jMenu1, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.jMenu1.text")); // NOI18N setBackground(new java.awt.Color(0, 0, 0)); addComponentListener(new java.awt.event.ComponentAdapter() { @@ -406,6 +434,30 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan } }); toolbar.add(zoomResetButton); + toolbar.add(jSeparator3); + + org.openide.awt.Mnemonics.setLocalizedText(createTagButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.createTagButton.text")); // NOI18N + createTagButton.setFocusable(false); + createTagButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + createTagButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + createTagButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + createTagButtonActionPerformed(evt); + } + }); + toolbar.add(createTagButton); + toolbar.add(jSeparator4); + + org.openide.awt.Mnemonics.setLocalizedText(deleteTagButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.deleteTagButton.text")); // NOI18N + deleteTagButton.setFocusable(false); + deleteTagButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + deleteTagButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + deleteTagButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + deleteTagButtonActionPerformed(evt); + } + }); + toolbar.add(deleteTagButton); add(toolbar); }// //GEN-END:initComponents @@ -450,9 +502,24 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan updateView(); }//GEN-LAST:event_formComponentResized + private void deleteTagButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteTagButtonActionPerformed + imageGroup.deleteNode(focusedNode); + }//GEN-LAST:event_deleteTagButtonActionPerformed + + private void createTagButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createTagButtonActionPerformed + + }//GEN-LAST:event_createTagButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton createTagButton; + private javax.swing.JButton deleteTagButton; + private javax.swing.JMenu jMenu1; + private javax.swing.JPopupMenu jPopupMenu1; + private javax.swing.JPopupMenu jPopupMenu2; private javax.swing.JToolBar.Separator jSeparator1; private javax.swing.JToolBar.Separator jSeparator2; + private javax.swing.JToolBar.Separator jSeparator3; + private javax.swing.JToolBar.Separator jSeparator4; private javax.swing.JButton rotateLeftButton; private javax.swing.JButton rotateRightButton; private javax.swing.JTextField rotationTextField; @@ -618,107 +685,4 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan rotationTextField.setText((int) rotation + "°"); zoomTextField.setText((Math.round(zoomRatio * 100.0)) + "%"); } - - /** - * Enables users to 'tag' a region of an image by clicking and dragging a - * rectangle overtop. - */ - class ImageTaggingTool extends Rectangle { - - private final double imageWidth; - private final double imageHeight; - private final double imageOriginX; - private final double imageOriginY; - - //Origin of the drag event. - private double rectangleOriginX; - private double rectangleOriginY; - - //Rectangle lines should be 1.5% of the image. This level of thickness has - //a good balance between visual acuity and loss of selection at the borders - //of the image. - private double lineThicknessAsPercent = 1.5; - - /** - * Adds tagging support to an image, where the 'tag' rectangle will be - * the specified color. - * - * @param image Image to tag - * @param color Color of the 'tag' rectangle - */ - private ImageTaggingTool(ImageView image, Color color) { - defaultSettings(); - - imageWidth = image.getImage().getWidth(); - imageHeight = image.getImage().getHeight(); - imageOriginX = image.getX(); - imageOriginY = image.getY(); - - setStroke(color); - setFill(color.deriveColor(0, 0, 0, 0)); - - //Calculate how many pixels the stroke width should be to guarentee - //a consistent % of image consumed by the rectangle border. - double min = Math.min(imageWidth, imageHeight); - double lineThicknessPixels = min * lineThicknessAsPercent / 100.0; - setStrokeWidth(lineThicknessPixels); - setVisible(false); - - //Create a rectangle by left clicking on the image - image.setOnMousePressed((MouseEvent event) -> { - if (event.isSecondaryButtonDown()) { - return; - } - - //Reset box on new click. - defaultSettings(); - - rectangleOriginX = event.getX(); - rectangleOriginY = event.getY(); - - setX(rectangleOriginX); - setY(rectangleOriginY); - }); - - //Adjust the rectangle by dragging the left mouse button - image.setOnMouseDragged((MouseEvent event) -> { - if (event.isSecondaryButtonDown()) { - return; - } - - /** - * Ensure the rectangle is contained within image boundaries and - * that the line thickness is kept within bounds. - */ - double newX = Math.min(Math.max(event.getX(), imageOriginX) - + lineThicknessPixels / 2, imageWidth - lineThicknessPixels / 2); - double newY = Math.min(Math.max(event.getY(), imageOriginY) - + lineThicknessPixels / 2, imageHeight - lineThicknessPixels / 2); - - setVisible(true); - double offsetX = newX - rectangleOriginX; - if (offsetX < 0) { - setX(newX); - } - setWidth(Math.abs(offsetX)); - - double offsetY = newY - rectangleOriginY; - if (offsetY < 0) { - setY(newY); - } - setHeight(Math.abs(offsetY)); - }); - } - - /** - * Reset the rectangle to default dimensions. - */ - public final void defaultSettings() { - setX(0); - setY(0); - setWidth(0); - setHeight(0); - setVisible(false); - } - } } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ControlType.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ControlType.java new file mode 100755 index 0000000000..6e0c6a2493 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ControlType.java @@ -0,0 +1,32 @@ +/* + * 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.contentviewers.imagetagging; + +import javafx.event.Event; +import javafx.event.EventType; + +/** + * + * @author dsmyda + */ +public class ControlType { + public static final EventType NOT_FOCUSED = new EventType<>("NOT_FOCUSED"); + public static final EventType FOCUSED = new EventType<>("FOCUSED"); + public static final EventType DELETE = new EventType<>("DELETE"); +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeEvent.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeEvent.java new file mode 100755 index 0000000000..e487e01061 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeEvent.java @@ -0,0 +1,49 @@ +/* + * 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.contentviewers.imagetagging; + +import java.util.EventObject; +import javafx.event.Event; +import javafx.event.EventType; +import javafx.scene.Node; + +/** + * + * @author dsmyda + */ +public final class FocusChangeEvent extends EventObject{ + + private final EventType type; + private final Node focused; + + public FocusChangeEvent(Object source, EventType type, Node focused) { + super(source); + this.type = type; + this.focused = focused; + } + + public EventType getType() { + return type; + } + + public Node getNode() { + return focused; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeListener.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeListener.java new file mode 100755 index 0000000000..6699e57002 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeListener.java @@ -0,0 +1,18 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.contentviewers.imagetagging; + +import java.util.EventListener; + +/** + * + * @author dsmyda + */ +@FunctionalInterface +public interface FocusChangeListener extends EventListener{ + + void focusChanged(FocusChangeEvent event); +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java new file mode 100755 index 0000000000..146fbe1281 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java @@ -0,0 +1,145 @@ +/* + * 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.contentviewers.imagetagging; + +import java.util.ArrayList; +import java.util.Collection; +import javafx.scene.image.ImageView; +import javafx.scene.input.MouseEvent; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; + +/** + * Creates image tags. This tool can be treated like any other JavaFX node. + */ +public final class ImageTagCreator extends Rectangle { + + //Origin of the drag event. + private double rectangleOriginX, rectangleOriginY; + + //Rectangle lines should be 1.5% of the image. This level of thickness has + //a good balance between visual acuity and loss of selection at the borders + //of the image. + private double lineThicknessAsPercent = 1.5; + private final double minArea; + private final Collection listeners; + + /** + * Adds tagging support to an image, where the 'tag' rectangle will be the + * specified color. + * + * @param image Image to tag + */ + public ImageTagCreator(ImageView image) { + listeners = new ArrayList<>(); + + setStroke(Color.RED); + setFill(Color.RED.deriveColor(0, 0, 0, 0)); + + //Calculate how many pixels the stroke width should be to guarentee + //a consistent % of image consumed by the rectangle border. + double min = Math.min(image.getImage().getWidth(), image.getImage().getHeight()); + double lineThicknessPixels = min * lineThicknessAsPercent / 100.0; + setStrokeWidth(lineThicknessPixels); + minArea = lineThicknessPixels * lineThicknessPixels; + setVisible(false); + + this.addEventHandler(ControlType.NOT_FOCUSED, (event) -> defaultSettings()); + + //Create a rectangle by left clicking on the image + image.setOnMousePressed((MouseEvent event) -> { + if (event.isSecondaryButtonDown()) { + return; + } + + //Reset box on new click. + defaultSettings(); + rectangleOriginX = event.getX(); + rectangleOriginY = event.getY(); + + setX(rectangleOriginX); + setY(rectangleOriginY); + }); + + //Adjust the rectangle by dragging the left mouse button + image.setOnMouseDragged((MouseEvent event) -> { + if (event.isSecondaryButtonDown()) { + return; + } + + double currentX = event.getX(), currentY = event.getY(); + + /** + * Ensure the rectangle is contained within image boundaries and + * that the line thickness is kept within bounds. + */ + double newX = Math.min(Math.max(currentX, image.getX()) + + lineThicknessPixels / 2, image.getImage().getWidth() - lineThicknessPixels / 2); + double newY = Math.min(Math.max(currentY, image.getY()) + + lineThicknessPixels / 2, image.getImage().getHeight() - lineThicknessPixels / 2); + + setVisible(true); + double offsetX = newX - rectangleOriginX; + if (offsetX < 0) { + setX(newX); + } + setWidth(Math.abs(offsetX)); + + double offsetY = newY - rectangleOriginY; + if (offsetY < 0) { + setY(newY); + } + setHeight(Math.abs(offsetY)); + }); + + image.setOnMouseReleased(event -> { + if ((this.getWidth() - this.getStrokeWidth()) * + (this.getHeight() - this.getStrokeWidth()) <= minArea) { + defaultSettings(); + return; + } + + //TODO - persist tag. + + //Notify listeners + StoredTagEvent newTagEvent = new StoredTagEvent(this, new StoredTag(image, this.getX(), this.getY(), + this.getX() + this.getWidth(), this.getY() + this.getHeight())); + + listeners.forEach((listener) -> { + listener.newTagEvent(newTagEvent); + }); + + defaultSettings(); + }); + } + + public void addNewTagListener(StoredTagListener listener) { + listeners.add(listener); + } + + /** + * Reset the rectangle to default dimensions. + */ + private void defaultSettings() { + setWidth(0); + setHeight(0); + setVisible(false); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTag.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTag.java new file mode 100755 index 0000000000..6670639811 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTag.java @@ -0,0 +1,239 @@ +/* + * 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.contentviewers.imagetagging; + +import com.sun.javafx.event.EventDispatchChainImpl; +import javafx.collections.ListChangeListener; +import javafx.scene.Cursor; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.image.ImageView; +import javafx.scene.input.MouseEvent; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; +import javafx.scene.shape.Rectangle; + +/** + * + * @author dsmyda + */ +public final class StoredTag extends Group { + + private final EventDispatchChainImpl ALL_CHILDREN; + private final PhysicalTag physicalTag; + + public StoredTag(ImageView image, double x, double y, double x1, double y1) { + ALL_CHILDREN = new EventDispatchChainImpl(); + + this.getChildren().addListener((ListChangeListener) change -> { + change.next(); + change.getAddedSubList().forEach((node) -> ALL_CHILDREN.append(node.getEventDispatcher())); + }); + + double min = Math.min(image.getImage().getWidth(), image.getImage().getHeight()); + double lineThicknessPixels = min * 1.5 / 100.0; + physicalTag = new PhysicalTag(image); + physicalTag.setStrokeWidth(lineThicknessPixels); + + EditHandle bottomLeft = new EditHandle(); + bottomLeft.setPosition(Position.bottom(), Position.left()); + bottomLeft.setDrag(Drag.bottom(), Drag.left()); + + EditHandle bottomRight = new EditHandle(); + bottomRight.setPosition(Position.bottom(), Position.right()); + bottomRight.setDrag(Drag.bottom(), Drag.right()); + + EditHandle topLeft = new EditHandle(); + topLeft.setPosition(Position.top(), Position.left()); + topLeft.setDrag(Drag.top(), Drag.left()); + + EditHandle topRight = new EditHandle(); + topRight.setPosition(Position.top(), Position.right()); + topRight.setDrag(Drag.top(), Drag.right()); + + EditHandle bottomMiddle = new EditHandle(); + bottomMiddle.setPosition(Position.bottom(), Position.xMiddle()); + bottomMiddle.setDrag(Drag.bottom()); + + EditHandle topMiddle = new EditHandle(); + topMiddle.setPosition(Position.top(), Position.xMiddle()); + topMiddle.setDrag(Drag.top()); + + EditHandle rightMiddle = new EditHandle(); + rightMiddle.setPosition(Position.right(), Position.yMiddle()); + rightMiddle.setDrag(Drag.right()); + + EditHandle leftMiddle = new EditHandle(); + leftMiddle.setPosition(Position.left(), Position.yMiddle()); + leftMiddle.setDrag(Drag.left()); + + this.getChildren().addAll(physicalTag, bottomLeft, bottomRight, topLeft, + topRight, bottomMiddle, topMiddle, rightMiddle, leftMiddle); + + //Position the tag on the image. The edit knobs will be notified of + //the new coords and adjust themselves. + physicalTag.setX(x); + physicalTag.setY(y); + physicalTag.setWidth(x1 - x); + physicalTag.setHeight(y1 - y); + + this.addEventHandler(ControlType.NOT_FOCUSED, event -> ALL_CHILDREN.dispatchEvent(event)); + this.addEventHandler(ControlType.FOCUSED, event -> ALL_CHILDREN.dispatchEvent(event)); + this.addEventHandler(ControlType.DELETE, event -> ALL_CHILDREN.dispatchEvent(event)); + } + + class PhysicalTag extends Rectangle { + + private final ImageView image; + + public PhysicalTag(ImageView image) { + this.setStroke(Color.RED); + this.setFill(Color.RED.deriveColor(0, 0, 0, 0)); + + this.addEventHandler(ControlType.NOT_FOCUSED, event -> this.setOpacity(1)); + this.addEventHandler(ControlType.FOCUSED, event -> this.setOpacity(0.5)); + + this.addEventHandler(ControlType.DELETE, event -> { + this.setVisible(false); + //TODO - delete tag from persistent storage here. + }); + + this.image = image; + } + + private void save() { + //TODO - persist tag + } + + private ImageView getUnderlyingImage() { + return image; + } + } + + class EditHandle extends Circle { + + public EditHandle() { + super(physicalTag.getStrokeWidth(), physicalTag.getStroke()); + this.setVisible(false); + + //Manipulate the parent rectangle when this knob is dragged. + this.addEventHandler(ControlType.NOT_FOCUSED, event -> this.setVisible(false)); + this.addEventHandler(ControlType.FOCUSED, event -> this.setVisible(true)); + this.addEventHandler(ControlType.DELETE, event -> this.setVisible(false)); + this.setOnDragDetected(event -> this.getParent().setCursor(Cursor.CLOSED_HAND)); + this.setOnMouseReleased(event -> { + this.getParent().setCursor(Cursor.DEFAULT); + physicalTag.save(); + }); + } + + public void setPosition(Position... vals) { + for (Position pos : vals) { + physicalTag.widthProperty().addListener((obs, oldVal, newVal) -> pos.set(physicalTag, this)); + physicalTag.heightProperty().addListener((obs, oldVal, newVal) -> pos.set(physicalTag, this)); + } + } + + public void setDrag(Drag... vals) { + this.setOnMouseDragged((event) -> { + for (Drag drag : vals) { + drag.perform(physicalTag, event, physicalTag.getUnderlyingImage()); + } + }); + } + } + + static interface Position { + + void set(Rectangle parent, Circle knob); + + static Position left() { + return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty()); + } + + static Position right() { + return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty().add(parent.getWidth())); + } + + static Position top() { + return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty()); + } + + static Position bottom() { + return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty().add(parent.getHeight())); + } + + static Position xMiddle() { + return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty().add(parent.getWidth() / 2)); + } + + static Position yMiddle() { + return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty().add(parent.getHeight() / 2)); + } + } + + static interface Drag { + + void perform(Rectangle parent, MouseEvent event, ImageView image); + + static Drag bottom() { + return (parent, event, image) -> { + double deltaY = event.getY() - parent.getY(); + if (deltaY > 0 && event.getY() + < image.getY() + image.getImage().getHeight() + - parent.getStrokeWidth() / 2) { + parent.setHeight(deltaY); + } + }; + } + + static Drag top() { + return (parent, event, image) -> { + double deltaY = parent.getY() + parent.getHeight() - event.getY(); + if (deltaY < parent.getY() + parent.getHeight() && event.getY() + > image.getY() + parent.getStrokeWidth() / 2 && deltaY > 0) { + parent.setHeight(deltaY); + parent.setY(event.getY()); + } + }; + } + + static Drag left() { + return (parent, event, image) -> { + double deltaX = parent.getX() + parent.getWidth() - event.getX(); + if (deltaX < parent.getX() + parent.getWidth() + && event.getX() > image.getX() + parent.getStrokeWidth() / 2 + && deltaX > 0) { + parent.setWidth(deltaX); + parent.setX(event.getX()); + } + }; + } + + static Drag right() { + return (parent, event, image) -> { + double deltaX = event.getX() - parent.getX(); + if (deltaX > 0 && event.getX() < image.getX() + + image.getImage().getWidth() - parent.getStrokeWidth() / 2) { + parent.setWidth(deltaX); + } + }; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagEvent.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagEvent.java new file mode 100755 index 0000000000..e1cba2d7f6 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagEvent.java @@ -0,0 +1,40 @@ +/* + * 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.contentviewers.imagetagging; + + +import java.util.EventObject; + +/** + * + * @author dsmyda + */ +public final class StoredTagEvent extends EventObject { + private final StoredTag tag; + + public StoredTagEvent(Object source, StoredTag tag) { + super(source); + this.tag = tag; + } + + public StoredTag getTag() { + return tag; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagListener.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagListener.java new file mode 100755 index 0000000000..a727cc4f53 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagListener.java @@ -0,0 +1,33 @@ +/* + * 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.contentviewers.imagetagging; + + +import java.util.EventListener; + +/** + * + * @author dsmyda + */ +@FunctionalInterface +public interface StoredTagListener extends EventListener { + + void newTagEvent(StoredTagEvent tagEvent); +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/TopLevelTagsGroup.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/TopLevelTagsGroup.java new file mode 100755 index 0000000000..6b7bca78e8 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/TopLevelTagsGroup.java @@ -0,0 +1,98 @@ +/* + * 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.contentviewers.imagetagging; + +import com.sun.javafx.event.EventDispatchChainImpl; +import java.util.ArrayList; +import java.util.Collection; +import javafx.event.Event; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.image.ImageView; +import javafx.scene.input.MouseEvent; + +/** + * Top level group containing Image and all existing tags. + */ +public final class TopLevelTagsGroup extends Group { + private final EventDispatchChainImpl NO_OP_CHAIN = new EventDispatchChainImpl(); + private final Collection listeners; + + private Node lastFocus; + private final ImageView baseImage; + + public TopLevelTagsGroup(ImageView image) { + super(image); + baseImage = image; + listeners = new ArrayList<>(); + + //Manage focus, such that only one child can be set at a time. + this.addEventFilter(MouseEvent.MOUSE_PRESSED, (MouseEvent e) -> { + if (!e.isPrimaryButtonDown()) { + return; + } + + Node child = getTopLevelChild(e.getPickResult().getIntersectedNode()); + if (lastFocus == child) { + return; + } else if (lastFocus != null && lastFocus != child) { + resetFocus(lastFocus); + } + + child.getEventDispatcher().dispatchEvent(new Event(ControlType.FOCUSED), NO_OP_CHAIN); + listeners.forEach((listener) -> { + listener.focusChanged(new FocusChangeEvent(this, ControlType.FOCUSED, child)); + }); + + if(child != baseImage) { + child.toFront(); + } + lastFocus = child; + }); + } + + public void addFocusChangeListener(FocusChangeListener fcl) { + listeners.add(fcl); + } + + private void resetFocus(Node n) { + n.getEventDispatcher().dispatchEvent(new Event(ControlType.NOT_FOCUSED), NO_OP_CHAIN); + listeners.forEach((listener) -> { + listener.focusChanged(new FocusChangeEvent(this, ControlType.NOT_FOCUSED, n)); + }); + } + + public void deleteNode(Node dest) { + if(lastFocus == dest) { + resetFocus(lastFocus); + lastFocus = null; + } + + dest.getEventDispatcher().dispatchEvent(new Event(ControlType.DELETE), NO_OP_CHAIN); + } + + private Node getTopLevelChild(Node selected) { + Node curr = selected; + while (!this.getChildren().contains(curr)) { + curr = curr.getParent(); + } + return curr; + } +} From 40689ae751a31020828443b03951933be30b0c87 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Tue, 28 May 2019 12:48:38 -0400 Subject: [PATCH 002/106] Continued development checkpoint, more work to be done. --- .../ApplicationTagsManager.java | 67 +++++++++++++++ .../autopsy/contentviewers/Bundle.properties | 4 + .../contentviewers/Bundle.properties-MERGED | 4 + .../contentviewers/MediaViewImagePanel.form | 21 ++++- .../contentviewers/MediaViewImagePanel.java | 82 ++++++++++++++++--- .../imagetagging/FocusChangeEvent.java | 7 +- .../imagetagging/ImageTagCreator.java | 48 +++++++---- .../imagetagging/StoredTag.java | 30 +++++++ .../imagetagging/TopLevelTagsGroup.java | 73 +++++++++-------- 9 files changed, 271 insertions(+), 65 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ApplicationTagsManager.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ApplicationTagsManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ApplicationTagsManager.java new file mode 100755 index 0000000000..65756d16c8 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ApplicationTagsManager.java @@ -0,0 +1,67 @@ +/* + * 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.casemodule.services.applicationtags; + +import java.util.EnumSet; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.CaseDbAccessManager; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * A per case Autopsy service that manages the addition of application content + * tags to the case database. + */ +public final class ApplicationTagsManager { + + private static CaseDbAccessManager dbManager; + + private static final String TABLE_NAME = "beta_tag_app_data"; + private static final String TABLE_SCHEMA = "(app_data_id INTEGER PRIMARY KEY, " + + "content_tag_id INTEGER NOT NULL, app_data TEXT NOT NULL," + + "FOREIGN KEY(content_tag_id) REFERENCES content_tags(tag_id))"; + + private final String INSERT_OR_REPLACE_TAG_DATA = "(content_tag_id, app_data) VALUES (?, ?)"; + private final String SELECT_TAG_DATA = "* FROM " + TABLE_NAME + " WHERE content_tag_id = ?"; + private final String DELETE_TAG_DATA = "WHERE app_data_id = ?"; + + static { + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), evt -> { + if(evt.getNewValue() != null) { + Case currentCase = (Case) evt.getNewValue(); + try { + CaseDbAccessManager caseDb = currentCase.getSleuthkitCase().getCaseDbAccessManager(); + //Create our custom application tags table, if need be. + if (!caseDb.tableExists(TABLE_NAME)) { + caseDb.createTable(TABLE_NAME, TABLE_SCHEMA); + } + + dbManager = caseDb; + } catch (TskCoreException ex) { + //Log + } + } + }); + } + + //public createTag + //public updateTag + //public getTag + //public deleteTag + +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index 652f8781ba..631bb91273 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -89,3 +89,7 @@ MediaPlayerPanel.VolumeIcon.text=\ \ \ \ \ Volume MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00 MediaPlayerPanel.playButton.text=\u25ba MediaPlayerPanel.infoLabel.text=No Errors +MediaViewImagePanel.deleteTagButton.text=Delete Tag +MediaViewImagePanel.createTagButton.text=Create Tag +MediaViewImagePanel.jMenu1.text=jMenu1 +MediaViewImagePanel.editTagButton.text=Edit Tag Info diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index 236fddfada..b18b79580c 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -151,6 +151,10 @@ MediaPlayerPanel.VolumeIcon.text=\ \ \ \ \ Volume MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00 MediaPlayerPanel.playButton.text=\u25ba MediaPlayerPanel.infoLabel.text=No Errors +MediaViewImagePanel.deleteTagButton.text=Delete Tag +MediaViewImagePanel.createTagButton.text=Create Tag +MediaViewImagePanel.jMenu1.text=jMenu1 +MediaViewImagePanel.editTagButton.text=Edit Tag Info # {0} - tableName SQLiteViewer.readTable.errorText=Error getting rows for table: {0} # {0} - tableName diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form index fb17c441db..ccfe83b2cc 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form @@ -44,7 +44,7 @@ - + @@ -227,12 +227,15 @@ + + + @@ -242,12 +245,28 @@ + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index ebf7f48444..b6ec14fba1 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -52,15 +52,21 @@ import javafx.scene.transform.Scale; import javafx.scene.transform.Translate; import javax.imageio.ImageIO; import javax.swing.JPanel; +import javax.swing.SwingUtilities; import org.controlsfx.control.MaskerPane; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.python.google.common.collect.Lists; +import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog; +import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog.TagNameAndComment; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.contentviewers.imagetagging.ControlType; import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.TskCoreException; /** * Image viewer part of the Media View layered pane. Uses JavaFX to display the @@ -77,10 +83,11 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private final boolean fxInited; private JFXPanel fxPanel; + private AbstractFile file; + private StoredTag lastFocused; private TopLevelTagsGroup imageGroup; private ImageTagCreator tagger; private ImageView fxImageView; - private Node focusedNode; private ScrollPane scrollPane; private final ProgressBar progressBar = new ProgressBar(); private final MaskerPane maskerPane = new MaskerPane(); @@ -127,14 +134,17 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan fxImageView = new ImageView(); // will hold image imageGroup = new TopLevelTagsGroup(fxImageView); deleteTagButton.setEnabled(false); + editTagButton.setEnabled(false); imageGroup.addFocusChangeListener((event) -> { - if(event.getType() == ControlType.NOT_FOCUSED || event.getNode() == fxImageView) { + if (event.getType() == ControlType.NOT_FOCUSED || event.getNode().equals(fxImageView)) { deleteTagButton.setEnabled(false); createTagButton.setEnabled(true); + editTagButton.setEnabled(false); } else if (event.getType() == ControlType.FOCUSED) { deleteTagButton.setEnabled(true); + editTagButton.setEnabled(true); createTagButton.setEnabled(false); - focusedNode = event.getNode(); + lastFocused = event.getNode(); } }); scrollPane = new ScrollPane(imageGroup); // scrolls and sizes imageview @@ -220,16 +230,10 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan Image fxImage = readImageTask.get(); imageGroup.getChildren().clear(); imageGroup.getChildren().add(fxImageView); + this.file = file; if (nonNull(fxImage)) { // We have a non-null image, so let's show it. fxImageView.setImage(fxImage); - tagger = new ImageTagCreator(fxImageView); - StoredTagListener newTagListener = (StoredTagEvent evt) -> { - StoredTag tag = evt.getTag(); - imageGroup.getChildren().add(tag); - }; - tagger.addNewTagListener(newTagListener); - imageGroup.getChildren().add(tagger); resetView(); scrollPane.setContent(imageGroup); } else { @@ -326,8 +330,11 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan jSeparator2 = new javax.swing.JToolBar.Separator(); zoomResetButton = new javax.swing.JButton(); jSeparator3 = new javax.swing.JToolBar.Separator(); + filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0)); createTagButton = new javax.swing.JButton(); jSeparator4 = new javax.swing.JToolBar.Separator(); + editTagButton = new javax.swing.JButton(); + jSeparator5 = new javax.swing.JToolBar.Separator(); deleteTagButton = new javax.swing.JButton(); org.openide.awt.Mnemonics.setLocalizedText(jMenu1, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.jMenu1.text")); // NOI18N @@ -435,9 +442,11 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan }); toolbar.add(zoomResetButton); toolbar.add(jSeparator3); + toolbar.add(filler1); org.openide.awt.Mnemonics.setLocalizedText(createTagButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.createTagButton.text")); // NOI18N createTagButton.setFocusable(false); + createTagButton.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); createTagButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); createTagButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); createTagButton.addActionListener(new java.awt.event.ActionListener() { @@ -448,8 +457,21 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan toolbar.add(createTagButton); toolbar.add(jSeparator4); + org.openide.awt.Mnemonics.setLocalizedText(editTagButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.editTagButton.text")); // NOI18N + editTagButton.setFocusable(false); + editTagButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + editTagButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + editTagButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + editTagButtonActionPerformed(evt); + } + }); + toolbar.add(editTagButton); + toolbar.add(jSeparator5); + org.openide.awt.Mnemonics.setLocalizedText(deleteTagButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.deleteTagButton.text")); // NOI18N deleteTagButton.setFocusable(false); + deleteTagButton.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); deleteTagButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); deleteTagButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); deleteTagButton.addActionListener(new java.awt.event.ActionListener() { @@ -503,16 +525,53 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan }//GEN-LAST:event_formComponentResized private void deleteTagButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteTagButtonActionPerformed - imageGroup.deleteNode(focusedNode); + Platform.runLater(() -> { + imageGroup.deleteNode(lastFocused); + }); + try { + Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(lastFocused.getContentTag()); + } catch (TskCoreException ex) { + + } }//GEN-LAST:event_deleteTagButtonActionPerformed private void createTagButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createTagButtonActionPerformed + Platform.runLater(() -> { + tagger = new ImageTagCreator(fxImageView); + StoredTagListener newTagListener = (StoredTagEvent event) -> { + StoredTag tag = event.getTag(); + imageGroup.getChildren().add(tag); + SwingUtilities.invokeLater(() -> { + TagNameAndComment result = GetTagNameAndCommentDialog.doDialog(); + if (result != null) { + try { + ContentTag t = Case.getCurrentCase().getServices().getTagsManager().addContentTag(file, + result.getTagName(), result.getComment()); + tag.addContentTag(t); + } catch (TskCoreException ex) { + Platform.runLater(() -> imageGroup.deleteNode(tag)); + } + } else { + Platform.runLater(() -> imageGroup.deleteNode(tag)); + } + }); + imageGroup.deleteNode(tagger); + }; + tagger.addNewTagListener(newTagListener); + imageGroup.getChildren().add(tagger); + }); }//GEN-LAST:event_createTagButtonActionPerformed + private void editTagButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editTagButtonActionPerformed + ContentTag t = lastFocused.getContentTag(); + }//GEN-LAST:event_editTagButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton createTagButton; private javax.swing.JButton deleteTagButton; + private javax.swing.JButton editTagButton; + private javax.swing.Box.Filler filler1; private javax.swing.JMenu jMenu1; private javax.swing.JPopupMenu jPopupMenu1; private javax.swing.JPopupMenu jPopupMenu2; @@ -520,6 +579,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private javax.swing.JToolBar.Separator jSeparator2; private javax.swing.JToolBar.Separator jSeparator3; private javax.swing.JToolBar.Separator jSeparator4; + private javax.swing.JToolBar.Separator jSeparator5; private javax.swing.JButton rotateLeftButton; private javax.swing.JButton rotateRightButton; private javax.swing.JTextField rotationTextField; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeEvent.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeEvent.java index e487e01061..f8cac1a128 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeEvent.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeEvent.java @@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.contentviewers.imagetagging; import java.util.EventObject; import javafx.event.Event; import javafx.event.EventType; -import javafx.scene.Node; /** * @@ -30,9 +29,9 @@ import javafx.scene.Node; public final class FocusChangeEvent extends EventObject{ private final EventType type; - private final Node focused; + private final StoredTag focused; - public FocusChangeEvent(Object source, EventType type, Node focused) { + public FocusChangeEvent(Object source, EventType type, StoredTag focused) { super(source); this.type = type; this.focused = focused; @@ -42,7 +41,7 @@ public final class FocusChangeEvent extends EventObject{ return type; } - public Node getNode() { + public StoredTag getNode() { return focused; } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java index 146fbe1281..b0c114ada5 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.contentviewers.imagetagging; import java.util.ArrayList; import java.util.Collection; +import javafx.event.EventHandler; import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; import javafx.scene.paint.Color; @@ -40,6 +41,10 @@ public final class ImageTagCreator extends Rectangle { private double lineThicknessAsPercent = 1.5; private final double minArea; private final Collection listeners; + + private final EventHandler mousePressed; + private final EventHandler mouseDragged; + private final EventHandler mouseReleased; /** * Adds tagging support to an image, where the 'tag' rectangle will be the @@ -60,11 +65,8 @@ public final class ImageTagCreator extends Rectangle { setStrokeWidth(lineThicknessPixels); minArea = lineThicknessPixels * lineThicknessPixels; setVisible(false); - - this.addEventHandler(ControlType.NOT_FOCUSED, (event) -> defaultSettings()); - - //Create a rectangle by left clicking on the image - image.setOnMousePressed((MouseEvent event) -> { + + this.mousePressed = (MouseEvent event) -> { if (event.isSecondaryButtonDown()) { return; } @@ -76,10 +78,11 @@ public final class ImageTagCreator extends Rectangle { setX(rectangleOriginX); setY(rectangleOriginY); - }); - - //Adjust the rectangle by dragging the left mouse button - image.setOnMouseDragged((MouseEvent event) -> { + }; + + image.addEventHandler(MouseEvent.MOUSE_PRESSED, this.mousePressed); + + this.mouseDragged = (MouseEvent event) -> { if (event.isSecondaryButtonDown()) { return; } @@ -107,16 +110,16 @@ public final class ImageTagCreator extends Rectangle { setY(newY); } setHeight(Math.abs(offsetY)); - }); - - image.setOnMouseReleased(event -> { + }; + + image.addEventHandler(MouseEvent.MOUSE_DRAGGED, this.mouseDragged); + + this.mouseReleased = event -> { if ((this.getWidth() - this.getStrokeWidth()) * (this.getHeight() - this.getStrokeWidth()) <= minArea) { defaultSettings(); return; - } - - //TODO - persist tag. + } //Notify listeners StoredTagEvent newTagEvent = new StoredTagEvent(this, new StoredTag(image, this.getX(), this.getY(), @@ -127,7 +130,22 @@ public final class ImageTagCreator extends Rectangle { }); defaultSettings(); + }; + + image.addEventHandler(MouseEvent.MOUSE_RELEASED, this.mouseReleased); + this.addEventHandler(ControlType.NOT_FOCUSED, (event) -> { + defaultSettings(); + image.removeEventHandler(MouseEvent.MOUSE_RELEASED, mouseReleased); + image.removeEventHandler(MouseEvent.MOUSE_DRAGGED, mouseDragged); + image.removeEventHandler(MouseEvent.MOUSE_PRESSED, mousePressed); }); + + this.addEventHandler(ControlType.DELETE, event -> { + defaultSettings(); + image.removeEventHandler(MouseEvent.MOUSE_RELEASED, mouseReleased); + image.removeEventHandler(MouseEvent.MOUSE_DRAGGED, mouseDragged); + image.removeEventHandler(MouseEvent.MOUSE_PRESSED, mousePressed); + }); } public void addNewTagListener(StoredTagListener listener) { diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTag.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTag.java index 6670639811..1a97918dee 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTag.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTag.java @@ -28,6 +28,7 @@ import javafx.scene.input.MouseEvent; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.shape.Rectangle; +import org.sleuthkit.datamodel.ContentTag; /** * @@ -92,15 +93,32 @@ public final class StoredTag extends Group { physicalTag.setY(y); physicalTag.setWidth(x1 - x); physicalTag.setHeight(y1 - y); + + this.focusedProperty().addListener((ov, oldV, newV) -> { + if(!newV) { + System.out.println("NOT FOCUSED"); + } else { + System.out.println("GAINED FOCUS"); + } + }); this.addEventHandler(ControlType.NOT_FOCUSED, event -> ALL_CHILDREN.dispatchEvent(event)); this.addEventHandler(ControlType.FOCUSED, event -> ALL_CHILDREN.dispatchEvent(event)); this.addEventHandler(ControlType.DELETE, event -> ALL_CHILDREN.dispatchEvent(event)); } + + public void addContentTag(ContentTag t) { + physicalTag.linkContentTag(t); + } + + public ContentTag getContentTag() { + return physicalTag.getContentTag(); + } class PhysicalTag extends Rectangle { private final ImageView image; + private ContentTag tag; public PhysicalTag(ImageView image) { this.setStroke(Color.RED); @@ -120,10 +138,22 @@ public final class StoredTag extends Group { private void save() { //TODO - persist tag } + + public void linkContentTag(ContentTag t) { + tag = t; + } + + public ContentTag getContentTag() { + return tag; + } private ImageView getUnderlyingImage() { return image; } + + private class ImageRegion { + + } } class EditHandle extends Circle { diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/TopLevelTagsGroup.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/TopLevelTagsGroup.java index 6b7bca78e8..2a00df6f7a 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/TopLevelTagsGroup.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/TopLevelTagsGroup.java @@ -16,7 +16,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.sleuthkit.autopsy.contentviewers.imagetagging; import com.sun.javafx.event.EventDispatchChainImpl; @@ -32,6 +31,7 @@ import javafx.scene.input.MouseEvent; * Top level group containing Image and all existing tags. */ public final class TopLevelTagsGroup extends Group { + private final EventDispatchChainImpl NO_OP_CHAIN = new EventDispatchChainImpl(); private final Collection listeners; @@ -49,50 +49,55 @@ public final class TopLevelTagsGroup extends Group { return; } - Node child = getTopLevelChild(e.getPickResult().getIntersectedNode()); - if (lastFocus == child) { - return; - } else if (lastFocus != null && lastFocus != child) { - resetFocus(lastFocus); + Node topLevelChild = e.getPickResult().getIntersectedNode(); + while (!this.getChildren().contains(topLevelChild)) { + topLevelChild = topLevelChild.getParent(); } - - child.getEventDispatcher().dispatchEvent(new Event(ControlType.FOCUSED), NO_OP_CHAIN); - listeners.forEach((listener) -> { - listener.focusChanged(new FocusChangeEvent(this, ControlType.FOCUSED, child)); - }); - - if(child != baseImage) { - child.toFront(); - } - lastFocus = child; + + requestFocus(topLevelChild); }); } - + public void addFocusChangeListener(FocusChangeListener fcl) { listeners.add(fcl); } - + private void resetFocus(Node n) { n.getEventDispatcher().dispatchEvent(new Event(ControlType.NOT_FOCUSED), NO_OP_CHAIN); - listeners.forEach((listener) -> { - listener.focusChanged(new FocusChangeEvent(this, ControlType.NOT_FOCUSED, n)); - }); - } - - public void deleteNode(Node dest) { - if(lastFocus == dest) { - resetFocus(lastFocus); - lastFocus = null; + if(n instanceof StoredTag) { + listeners.forEach((listener) -> { + listener.focusChanged(new FocusChangeEvent(this, ControlType.NOT_FOCUSED, (StoredTag) n)); + }); } - - dest.getEventDispatcher().dispatchEvent(new Event(ControlType.DELETE), NO_OP_CHAIN); } - private Node getTopLevelChild(Node selected) { - Node curr = selected; - while (!this.getChildren().contains(curr)) { - curr = curr.getParent(); + public void deleteNode(Node n) { + if (lastFocus == n) { + resetFocus(n); + lastFocus = null; } - return curr; + n.getEventDispatcher().dispatchEvent(new Event(ControlType.DELETE), NO_OP_CHAIN); + this.getChildren().remove(n); + } + + public void requestFocus(Node n) { + if (lastFocus == n) { + return; + } else if (lastFocus != null && lastFocus != n) { + resetFocus(lastFocus); + } + + n.getEventDispatcher().dispatchEvent(new Event(ControlType.FOCUSED), NO_OP_CHAIN); + if(n instanceof StoredTag) { + listeners.forEach((listener) -> { + listener.focusChanged(new FocusChangeEvent(this, ControlType.FOCUSED, (StoredTag) n)); + }); + } + + if (n != baseImage) { + n.toFront(); + } + + lastFocus = n; } } From 85a3124f9d5765e5822d53092d44345131692ecd Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 28 May 2019 16:20:03 -0400 Subject: [PATCH 003/106] 5061 add initial commit of Bing translator --- Core/ivy.xml | 3 + Core/nbproject/project.properties | 4 + Core/nbproject/project.xml | 8 + .../translators/BingTranslator.java | 158 ++++++++++++++++++ .../translators/BingTranslatorTest.java | 89 ++++++++++ 5 files changed, 262 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java create mode 100644 Core/test/unit/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorTest.java diff --git a/Core/ivy.xml b/Core/ivy.xml index 9dfd0fcf85..54ed532feb 100644 --- a/Core/ivy.xml +++ b/Core/ivy.xml @@ -45,6 +45,9 @@ + + + diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index cf21c9a068..74a08fd53d 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -111,6 +111,10 @@ file.reference.grpc-context-1.19.0.jar=release/modules/ext/grpc-context-1.19.0.j file.reference.opencensus-api-0.19.2.jar=release/modules/ext/opencensus-api-0.19.2.jar file.reference.opencensus-contrib-http-util-0.19.2.jar=release/modules/ext/opencensus-contrib-http-util-0.19.2.jar file.reference.threetenbp-1.3.3.jar=release/modules/ext/threetenbp-1.3.3.jar +file.reference.okhttp-2.7.5-javadoc.jar=release/modules/ext/okhttp-2.7.5-javadoc.jar +file.reference.okhttp-2.7.5-sources.jar=release/modules/ext/okhttp-2.7.5-sources.jar +file.reference.okhttp-2.7.5.jar=release/modules/ext/okhttp-2.7.5.jar +file.reference.okio-1.6.0.jar=release/modules/ext/okio-1.6.0.jar javac.source=1.8 javac.compilerargs=-Xlint -Xlint:-serial license.file=../LICENSE-2.0.txt diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index 2a2fcd6676..624421be48 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -773,6 +773,14 @@ ext/google-api-services-translate-v2-rev20170525-1.27.0.jar release/modules/ext/google-api-services-translate-v2-rev20170525-1.27.0.jar + + ext/okhttp-2.7.5.jar + release/modules/ext/okhttp-2.7.5.jar + + + ext/okio-1.6.0.jar + release/modules/ext/okio-1.6.0.jar + diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java new file mode 100644 index 0000000000..892ecb5ac6 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java @@ -0,0 +1,158 @@ +/* + * 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.texttranslation.translators; + +import java.io.*; +import com.google.gson.*; +import com.squareup.okhttp.*; +import java.awt.Component; +import javax.swing.JLabel; +import org.openide.util.NbBundle.Messages; + +/** + * Translates text by making HTTP requests to Bing Translator. + * This requires a valid subscription key for a Microsoft Azure account. + */ +@ServiceProvider(service = TextTranslator.class) +public class BingTranslator implements TextTranslator{ + // Insert the subscription key here. + private String subscriptionKey = ""; + + //In the String below, "en" is the target language. You can include multiple target + //languages separated by commas. A full list of supported languages is here: + //https://docs.microsoft.com/en-us/azure/cognitive-services/translator/language-support + private static final String URL = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=en"; + + // This sends messages to Microsoft. + private final OkHttpClient CLIENT = new OkHttpClient(); + + //We might want to make this a configurable setting for anyone who has a + //paid account that's willing to pay for long documents. + private final int MAX_STRING_LENGTH = 5000; + + /** + * Converts an input test to the JSON format required by Bing Translator, + * posts it to Microsoft, and returns the JSON text response. + * + * @param string The input text to be translated. + * @return The translation response as a JSON string + * @throws IOException if the request could not be executed due to cancellation, a connectivity problem or timeout. + */ + public String postTranslationRequest(String string) throws IOException { + MediaType mediaType = MediaType.parse("application/json"); + + JsonArray jsonArray = new JsonArray(); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("Text", string); + jsonArray.add(jsonObject); + String bodyString = jsonArray.toString(); + + RequestBody body = RequestBody.create(mediaType, + bodyString); + Request request = new Request.Builder() + .url(URL).post(body) + .addHeader("Ocp-Apim-Subscription-Key", subscriptionKey) + .addHeader("Content-type", "application/json").build(); + Response response = CLIENT.newCall(request).execute(); + return response.body().string(); + } + + @Override + public String translate(String string) throws TranslationException { + if (subscriptionKey == null || subscriptionKey.isEmpty()) { + throw new TranslationException("Bing Translator has not been configured, credentials need to be specified"); + } + + //Translates some text into English, without specifying the source langauge. + + // HTML files were producing lots of white space at the end + string = string.trim(); + + //Google Translate required us to replace (\r\n|\n) with
+ //but Bing Translator doesn not have that requirement. + + //The free account has a maximum file size. If you have a paid account, + //you probably still want to limit file size to prevent accidentally + //translating very large documents. + if (string.length() > MAX_STRING_LENGTH) { + string = string.substring(0, MAX_STRING_LENGTH); + } + + try { + String response = postTranslationRequest(string); + return parseJSONResponse(response); + } catch (Throwable e) { + throw new TranslationException(e.getMessage()); + } + } + + @Messages({"BingTranslator.name.text=Bing Translator"}) + @Override + public String getName() { + return Bundle.BingTranslator_name_text(); + } + + @Override + public Component getComponent() { + return new JLabel("There are no settings to configure for Bing Translator"); + } + + @Override + public void saveSettings() { + //There are no settings to configure for Bing Translator + //Possible settings for the future: + //source language, target language, API key, path to JSON file of API key. + //We'll need test code to make sure that exceptions are thrown in all of + //those scenarios. + return; + } + + private String parseJSONResponse(String json_text) throws TranslationException { + /* Here is an example of the text we get from Bing when input is "gato", + the Spanish word for cat: + [ + { + "detectedLanguage": { + "language": "es", + "score": 1.0 + }, + "translations": [ + { + "text": "cat", + "to": "en" + } + ] + } + ] + */ + JsonParser parser = new JsonParser(); + try { + JsonArray responses = parser.parse(json_text).getAsJsonArray(); + //As far as I know, there's always exactly one item in the array. + JsonObject response0 = responses.get(0).getAsJsonObject(); + JsonArray translations = response0.getAsJsonArray("translations"); + JsonObject translation0 = translations.get(0).getAsJsonObject(); + String text = translation0.get("text").getAsString(); + return text; + } catch (IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException e) { + throw new TranslationException("JSON text does not match Bing Translator scheme: " + e); + } + } +} \ No newline at end of file diff --git a/Core/test/unit/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorTest.java b/Core/test/unit/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorTest.java new file mode 100644 index 0000000000..97512faa3c --- /dev/null +++ b/Core/test/unit/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorTest.java @@ -0,0 +1,89 @@ + +/* + * 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.texttranslation.translators; + +import java.io.IOException; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +public class BingTranslatorTest { + @Test + public void testTranslate() throws Exception { + BingTranslator translator = new BingTranslator(); + String input = "gato"; + String expectedTranslation = "cat"; + runTest(translator, input, expectedTranslation); + } + + @Test + public void testQuickStartSentence() throws Exception { + BingTranslator translator = new BingTranslator(); + String input = "Willkommen bei Microsoft Translator. Raten Sie mal, wie viele Sprachen ich spreche."; + String expectedTranslation = "Welcome to Microsoft Translator. Guess how many languages I speak."; + runTest(translator, input, expectedTranslation); + } + + @Test + public void testCharacterEscapes() throws Exception { + BingTranslator translator = new BingTranslator(); + String input = "\"gato\"";; + String expectedTranslation = "Cat"; + runTest(translator, input, expectedTranslation); + } + + @Test + public void testLineBreaks() throws Exception { + BingTranslator translator = new BingTranslator(); + String input = "gato\nperro";; + String expectedTranslation = "cat\nDog"; + runTest(translator, input, expectedTranslation); + } + + /** + * Test whether translator throws an error. This should not be part of our + * regular testing, because we are limited to only 2MB of free translations + * ever. + * @param translator A BingTranslator + * @param input Text to translate + * @param expectedTranslation Not used unless you uncomment those lines. + */ + public void runTest(BingTranslator translator, String input, String expectedTranslation) { + String translation; + try { + translation = translator.translate(input); + } + catch (Throwable e) { + fail("Bing translation produced an exception: " + e.getMessage()); + return; + }; + + /* + //It's unrealistic to expect the same answer every time, but sometimes + //it's helpful to have this in your debug process. + System.out.println(translation); + assertEquals(expectedTranslation, translation); + */ + } +} From 1408fd289bd3aa7d61f93e7fa50b188b91f34e42 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 28 May 2019 18:08:24 -0400 Subject: [PATCH 004/106] 5061 allow Bing Translator credentials to be set by user --- .../translators/BingTranslator.java | 44 +++-- .../translators/BingTranslatorSettings.java | 80 ++++++++ .../BingTranslatorSettingsPanel.form | 85 +++++++++ .../BingTranslatorSettingsPanel.java | 171 ++++++++++++++++++ .../translators/Bundle.properties | 1 + .../translators/Bundle.properties-MERGED | 2 + .../translators/GoogleTranslator.java | 2 +- 7 files changed, 368 insertions(+), 17 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java create mode 100644 Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form create mode 100644 Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java index 892ecb5ac6..1f790d14c3 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java @@ -19,12 +19,20 @@ package org.sleuthkit.autopsy.texttranslation.translators; -import java.io.*; -import com.google.gson.*; -import com.squareup.okhttp.*; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.squareup.okhttp.MediaType; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.RequestBody; +import com.squareup.okhttp.Response; import java.awt.Component; -import javax.swing.JLabel; +import java.io.IOException; import org.openide.util.NbBundle.Messages; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.texttranslation.TextTranslator; +import org.sleuthkit.autopsy.texttranslation.TranslationException; /** * Translates text by making HTTP requests to Bing Translator. @@ -32,13 +40,13 @@ import org.openide.util.NbBundle.Messages; */ @ServiceProvider(service = TextTranslator.class) public class BingTranslator implements TextTranslator{ - // Insert the subscription key here. - private String subscriptionKey = ""; - //In the String below, "en" is the target language. You can include multiple target //languages separated by commas. A full list of supported languages is here: //https://docs.microsoft.com/en-us/azure/cognitive-services/translator/language-support private static final String URL = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=en"; + private final BingTranslatorSettingsPanel settingsPanel; + private final BingTranslatorSettings settings = new BingTranslatorSettings(); + // This sends messages to Microsoft. private final OkHttpClient CLIENT = new OkHttpClient(); @@ -47,6 +55,15 @@ public class BingTranslator implements TextTranslator{ //paid account that's willing to pay for long documents. private final int MAX_STRING_LENGTH = 5000; + + public BingTranslator(){ + settingsPanel = new BingTranslatorSettingsPanel(settings.getCredentials()); + } + + static String getMicrosftTranlatorUrl(){ + return URL; + } + /** * Converts an input test to the JSON format required by Bing Translator, * posts it to Microsoft, and returns the JSON text response. @@ -68,7 +85,7 @@ public class BingTranslator implements TextTranslator{ bodyString); Request request = new Request.Builder() .url(URL).post(body) - .addHeader("Ocp-Apim-Subscription-Key", subscriptionKey) + .addHeader("Ocp-Apim-Subscription-Key", settings.getCredentials()) .addHeader("Content-type", "application/json").build(); Response response = CLIENT.newCall(request).execute(); return response.body().string(); @@ -76,7 +93,7 @@ public class BingTranslator implements TextTranslator{ @Override public String translate(String string) throws TranslationException { - if (subscriptionKey == null || subscriptionKey.isEmpty()) { + if (settings.getCredentials() == null || settings.getCredentials().isEmpty()) { throw new TranslationException("Bing Translator has not been configured, credentials need to be specified"); } @@ -111,17 +128,12 @@ public class BingTranslator implements TextTranslator{ @Override public Component getComponent() { - return new JLabel("There are no settings to configure for Bing Translator"); + return settingsPanel; } @Override public void saveSettings() { - //There are no settings to configure for Bing Translator - //Possible settings for the future: - //source language, target language, API key, path to JSON file of API key. - //We'll need test code to make sure that exceptions are thrown in all of - //those scenarios. - return; + settings.setCredentials(settingsPanel.getCredentials()); } private String parseJSONResponse(String json_text) throws TranslationException { diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java new file mode 100644 index 0000000000..6d6ef47b39 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java @@ -0,0 +1,80 @@ +/* + * Autopsy + * + * 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.texttranslation.translators; + +import org.apache.commons.lang3.StringUtils; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; + +/** + * Class to handle the settings associated with the GoogleTranslator + */ +public final class BingTranslatorSettings { + + private static final String CREDENTIALS_KEY = "Credentials"; + private static final String BING_TRANSLATE_NAME = "BingTranslate"; + private static final String DEFAULT_CREDENTIALS = ""; + private String credentials; + + /** + * Construct a new GoogleTranslatorSettingsObject + */ + BingTranslatorSettings() { + loadSettings(); + } + + /** + * Get the path to the JSON credentials file + * + * @return the path to the credentials file + */ + String getCredentials() { + return credentials; + } + + /** + * Set the path to the JSON credentials file + * + * @param path the path to the credentials file + */ + void setCredentials(String creds) { + credentials = creds; + } + + + /** + * Load the settings into memory from their on disk storage + */ + void loadSettings() { + if (!ModuleSettings.configExists(BING_TRANSLATE_NAME)) { + ModuleSettings.makeConfigFile(BING_TRANSLATE_NAME); + } + if (ModuleSettings.settingExists(BING_TRANSLATE_NAME, CREDENTIALS_KEY)) { + credentials = ModuleSettings.getConfigSetting(BING_TRANSLATE_NAME, CREDENTIALS_KEY); + } else { + credentials = DEFAULT_CREDENTIALS; + } + } + + /** + * Save the setting from memory to their location on disk + */ + void saveSettings() { + ModuleSettings.setConfigSetting(BING_TRANSLATE_NAME, CREDENTIALS_KEY, credentials); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form new file mode 100644 index 0000000000..ae87b5a187 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java new file mode 100644 index 0000000000..ae8da0adcf --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java @@ -0,0 +1,171 @@ +/* + * Autopsy + * + * 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.texttranslation.translators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonParser; +import com.google.gson.JsonObject; +import com.squareup.okhttp.MediaType; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.RequestBody; +import com.squareup.okhttp.Response; +import java.io.IOException; +import java.util.logging.Logger; + +/** + * Settings panel for the GoogleTranslator + */ +public class BingTranslatorSettingsPanel extends javax.swing.JPanel { + + private static final Logger logger = Logger.getLogger(BingTranslatorSettingsPanel.class.getName()); + private static final long serialVersionUID = 1L; + + /** + * Creates new form GoogleTranslatorSettingsPanel + */ + public BingTranslatorSettingsPanel(String credentials) { + initComponents(); + credentialsField.setText(credentials); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + credentialsLabel = new javax.swing.JLabel(); + credentialsField = new javax.swing.JTextField(); + warningLabel = new javax.swing.JLabel(); + testButton = new javax.swing.JButton(); + + org.openide.awt.Mnemonics.setLocalizedText(credentialsLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.credentialsLabel.text")); // NOI18N + + warningLabel.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(warningLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.warningLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(testButton, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.testButton.text")); // NOI18N + testButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + testButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addComponent(credentialsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 86, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(credentialsField, javax.swing.GroupLayout.DEFAULT_SIZE, 463, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(testButton) + .addGap(8, 8, 8)))) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(credentialsLabel) + .addComponent(credentialsField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(testButton)) + .addGap(31, 31, 31) + .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(29, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void testButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_testButtonActionPerformed + if (testTranslationSetup()) { + warningLabel.setText(""); + } else { + warningLabel.setText("Invalid translation credentials"); + } + }//GEN-LAST:event_testButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTextField credentialsField; + private javax.swing.JLabel credentialsLabel; + private javax.swing.JButton testButton; + private javax.swing.JLabel warningLabel; + // End of variables declaration//GEN-END:variables + /** + * Converts an input test to the JSON format required by Bing Translator, + * posts it to Microsoft, and returns the JSON text response. + * + * @param string The input text to be translated. + * + * @return The translation response as a JSON string + * + * @throws IOException if the request could not be executed due to + * cancellation, a connectivity problem or timeout. + */ + private boolean testTranslationSetup() { + String testString = "forense"; + MediaType mediaType = MediaType.parse("application/json"); + + JsonArray jsonArray = new JsonArray(); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("Text", testString); + jsonArray.add(jsonObject); + String bodyString = jsonArray.toString(); + + RequestBody body = RequestBody.create(mediaType, + bodyString); + Request request = new Request.Builder() + .url(BingTranslator.getMicrosftTranlatorUrl()).post(body) + .addHeader("Ocp-Apim-Subscription-Key", credentialsField.getText()) + .addHeader("Content-type", "application/json").build(); + try { + Response response = new OkHttpClient().newCall(request).execute(); + JsonParser parser = new JsonParser(); + JsonArray responses = parser.parse(response.body().string()).getAsJsonArray(); + //As far as I know, there's always exactly one item in the array. + JsonObject response0 = responses.get(0).getAsJsonObject(); + JsonArray translations = response0.getAsJsonArray("translations"); + JsonObject translation0 = translations.get(0).getAsJsonObject(); + translation0.get("text").getAsString(); + return true; + } catch (IOException | IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException e) { + return false; + } + + } + + /** + * Get the currently set path to the JSON credentials file + * + * @return the path to the credentials file specified in the textarea + */ + String getCredentials() { + return credentialsField.getText(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties index 56782cdf33..024ddfadcd 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties @@ -2,3 +2,4 @@ GoogleTranslatorSettingsPanel.browseButton.text=Browse GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials: GoogleTranslatorSettingsPanel.warningLabel.text= GoogleTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: +BingTranslatorSettingsPanel.testButton.text=Test diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED index b0e30f632c..bfe84cb856 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED @@ -1,3 +1,4 @@ +BingTranslator.name.text=Bing Translator GoogleTranslator.name.text=Google Translate GoogleTranslatorSettingsPanel.browseButton.text=Browse GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials: @@ -11,3 +12,4 @@ GoogleTranslatorSettingsPanel.fileChooser.confirmButton=Select GoogleTranslatorSettingsPanel.json.description=JSON Files GoogleTranslatorSettingsPanel.warningLabel.text= GoogleTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: +BingTranslatorSettingsPanel.jButton1.text=Test diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java index 38506ef936..f40b2129e2 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java @@ -106,7 +106,7 @@ public final class GoogleTranslator implements TextTranslator { } /** - * Load the Google Cloud Translation service give the currently saved + * Load the Google Cloud Translation service given the currently saved * settings */ private void loadTranslator() { From f4e36841b116d35ed1a1c63409a6f13c359426fa Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 28 May 2019 18:09:24 -0400 Subject: [PATCH 005/106] 5061 minor ui panel tweak --- .../translators/BingTranslatorSettingsPanel.form | 4 ++-- .../translators/BingTranslatorSettingsPanel.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form index ae87b5a187..6acf950f40 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form @@ -44,9 +44,9 @@ - + - + diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java index ae8da0adcf..b49c3645b8 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java @@ -97,9 +97,9 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { .addComponent(credentialsLabel) .addComponent(credentialsField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(testButton)) - .addGap(31, 31, 31) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(29, Short.MAX_VALUE)) + .addContainerGap(49, Short.MAX_VALUE)) ); }// //GEN-END:initComponents From 5d63ab2bb8c76a37afbb5a7ec216c24b361aedb2 Mon Sep 17 00:00:00 2001 From: Raman Date: Wed, 29 May 2019 12:41:36 -0400 Subject: [PATCH 006/106] 4963: offload computation of S C & O columns to a background task, in order to speed up the loading of Data Results View table. --- .../datamodel/AbstractAbstractFileNode.java | 44 +++++++----- .../autopsy/datamodel/GetSCOTask.java | 71 +++++++++++++++++++ .../sleuthkit/autopsy/datamodel/SCOData.java | 56 +++++++++++++++ 3 files changed, 155 insertions(+), 16 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java create mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/SCOData.java diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index fa4529e44b..d6b589f977 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -82,7 +82,8 @@ public abstract class AbstractAbstractFileNode extends A private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE, Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, Case.Events.CR_COMMENT_CHANGED); - private static final ExecutorService translationPool; + // pool to run long running translation and getSCO tasks in backgound + private static final ExecutorService translationSCOPool; private static final Integer MAX_POOL_SIZE = 10; /** @@ -101,7 +102,7 @@ public abstract class AbstractAbstractFileNode extends A } if (UserPreferences.displayTranslatedFileNames()) { - AbstractAbstractFileNode.translationPool.submit(new TranslationTask( + AbstractAbstractFileNode.translationSCOPool.submit(new TranslationTask( new WeakReference<>(this), weakPcl)); } @@ -113,8 +114,8 @@ public abstract class AbstractAbstractFileNode extends A static { //Initialize this pool only once! This will be used by every instance of AAFN //to do their heavy duty SCO column and translation updates. - translationPool = Executors.newFixedThreadPool(MAX_POOL_SIZE, - new ThreadFactoryBuilder().setNameFormat("translation-task-thread-%d").build()); + translationSCOPool = Executors.newFixedThreadPool(MAX_POOL_SIZE, + new ThreadFactoryBuilder().setNameFormat("translation-and-sco-task-thread-%d").build()); } /** @@ -145,6 +146,7 @@ public abstract class AbstractAbstractFileNode extends A */ enum NodeSpecificEvents { TRANSLATION_AVAILABLE, + SCO_AVAILABLE } private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { @@ -223,6 +225,18 @@ public abstract class AbstractAbstractFileNode extends A //Set the tooltip this.setShortDescription(content.getName()); updateSheet(new NodeProperty<>(ORIGINAL_NAME.toString(), ORIGINAL_NAME.toString(), NO_DESCR, content.getName())); + } else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString())) { + SCOData scoData = (SCOData)evt.getNewValue(); + if (scoData.getScoreAndDescription() != null) { + updateSheet(new NodeProperty<>(SCORE.toString(), SCORE.toString(), scoData.getScoreAndDescription().getRight(), scoData.getScoreAndDescription().getLeft())); + } + if (scoData.getComment() != null) { + updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, scoData.getComment())); + } + if (scoData.getCountAndDescription() != null && + !UserPreferences.hideCentralRepoCommentsAndOccurrences()) { + updateSheet(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft())); + } } }; /** @@ -368,18 +382,16 @@ public abstract class AbstractAbstractFileNode extends A properties.add(new NodeProperty<>(ORIGINAL_NAME.toString(), ORIGINAL_NAME.toString(), NO_DESCR, "")); } - //SCO column prereq info.. - List tags = getContentTagsFromDatabase(); - CorrelationAttributeInstance attribute = getCorrelationAttributeInstance(); - - Pair scoreAndDescription = getScorePropertyAndDescription(tags); - properties.add(new NodeProperty<>(SCORE.toString(), SCORE.toString(), scoreAndDescription.getRight(), scoreAndDescription.getLeft())); - DataResultViewerTable.HasCommentStatus comment = getCommentProperty(tags, attribute); - properties.add(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, comment)); - if (!UserPreferences.hideCentralRepoCommentsAndOccurrences()) { - Pair countAndDescription = getCountPropertyAndDescription(attribute); - properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), countAndDescription.getRight(), countAndDescription.getLeft())); - } + // Create place holders for S C O + properties.add(new NodeProperty<>(SCORE.toString(), SCORE.toString(), NO_DESCR, "")); + properties.add(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, "")); + properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), NO_DESCR, "")); + + + // Get the SCO columns data in a background task + AbstractAbstractFileNode.translationSCOPool.submit(new GetSCOTask( + new WeakReference<>(this), weakPcl)); + properties.add(new NodeProperty<>(LOCATION.toString(), LOCATION.toString(), NO_DESCR, getContentPath(content))); properties.add(new NodeProperty<>(MOD_TIME.toString(), MOD_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getMtime(), content))); properties.add(new NodeProperty<>(CHANGED_TIME.toString(), CHANGED_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getCtime(), content))); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java new file mode 100644 index 0000000000..e8a7b5b1f5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java @@ -0,0 +1,71 @@ +/* + * 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.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.lang.ref.WeakReference; +import java.util.List; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.events.AutopsyEvent; +import org.sleuthkit.datamodel.ContentTag; + +/** + * Background task to get Score, Comment and Occurrences values for a Abstract file node. + * + */ +class GetSCOTask implements Runnable { + + private final WeakReference> weakNodeRef; + private final PropertyChangeListener listener; + + public GetSCOTask(WeakReference> weakContentRef, PropertyChangeListener listener) { + this.weakNodeRef = weakContentRef; + this.listener = listener; + } + + @Override + public void run() { + AbstractAbstractFileNode fileNode = weakNodeRef.get(); + + //Check for stale reference + if (fileNode == null) { + return; + } + + // get the SCO column values + List tags = fileNode.getContentTagsFromDatabase(); + CorrelationAttributeInstance attribute = fileNode.getCorrelationAttributeInstance(); + + SCOData scoData = new SCOData(); + scoData.setScoreAndDescription(fileNode.getScorePropertyAndDescription(tags)); + scoData.setComment(fileNode.getCommentProperty(tags, attribute)); + if (!UserPreferences.hideCentralRepoCommentsAndOccurrences()) { + scoData.setCountAndDescription(fileNode.getCountPropertyAndDescription(attribute)); + } + + if (listener != null) { + listener.propertyChange(new PropertyChangeEvent( + AutopsyEvent.SourceType.LOCAL.toString(), + AbstractAbstractFileNode.NodeSpecificEvents.SCO_AVAILABLE.toString(), + null, scoData)); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/SCOData.java b/Core/src/org/sleuthkit/autopsy/datamodel/SCOData.java new file mode 100644 index 0000000000..a9dd99369d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/SCOData.java @@ -0,0 +1,56 @@ +/* + * 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 org.apache.commons.lang3.tuple.Pair; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; + +/** + * Container to bag the S C & O data for an abstract file node. + * + */ +class SCOData { + + private Pair scoreAndDescription = null; + private DataResultViewerTable.HasCommentStatus comment = null; + private Pair countAndDescription = null; + + Pair getScoreAndDescription() { + return scoreAndDescription; + } + + DataResultViewerTable.HasCommentStatus getComment() { + return comment; + } + + Pair getCountAndDescription() { + return countAndDescription; + } + + void setScoreAndDescription(Pair scoreAndDescription) { + this.scoreAndDescription = scoreAndDescription; + } + void setComment(DataResultViewerTable.HasCommentStatus comment) { + this.comment = comment; + } + void setCountAndDescription(Pair countAndDescription) { + this.countAndDescription = countAndDescription; + } + +} From 5221f35fad788b85b6b2359ef756006f70196850 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 30 May 2019 11:24:04 -0400 Subject: [PATCH 007/106] 5061 disable bing tests since service won't have credentials --- ...guageWrapper.java => LanguageWrapper.java} | 0 .../translators/BingTranslatorTest.java | 117 +++++++++--------- 2 files changed, 60 insertions(+), 57 deletions(-) rename Core/src/org/sleuthkit/autopsy/texttranslation/translators/{GoogleLanguageWrapper.java => LanguageWrapper.java} (100%) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleLanguageWrapper.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/LanguageWrapper.java similarity index 100% rename from Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleLanguageWrapper.java rename to Core/src/org/sleuthkit/autopsy/texttranslation/translators/LanguageWrapper.java diff --git a/Core/test/unit/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorTest.java b/Core/test/unit/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorTest.java index 97512faa3c..48c29543d3 100644 --- a/Core/test/unit/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorTest.java +++ b/Core/test/unit/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorTest.java @@ -1,4 +1,3 @@ - /* * Autopsy Forensic Browser * @@ -17,7 +16,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.sleuthkit.autopsy.texttranslation.translators; import java.io.IOException; @@ -28,62 +26,67 @@ import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.*; -public class BingTranslatorTest { +/** + * Tests for the BingTranslator translation service, these tests have been + * commented out because they require credentials to perform + */ +public class BingTranslatorTest { + @Test public void testTranslate() throws Exception { - BingTranslator translator = new BingTranslator(); - String input = "gato"; - String expectedTranslation = "cat"; - runTest(translator, input, expectedTranslation); - } - - @Test - public void testQuickStartSentence() throws Exception { - BingTranslator translator = new BingTranslator(); - String input = "Willkommen bei Microsoft Translator. Raten Sie mal, wie viele Sprachen ich spreche."; - String expectedTranslation = "Welcome to Microsoft Translator. Guess how many languages I speak."; - runTest(translator, input, expectedTranslation); - } - - @Test - public void testCharacterEscapes() throws Exception { - BingTranslator translator = new BingTranslator(); - String input = "\"gato\"";; - String expectedTranslation = "Cat"; - runTest(translator, input, expectedTranslation); - } - - @Test - public void testLineBreaks() throws Exception { - BingTranslator translator = new BingTranslator(); - String input = "gato\nperro";; - String expectedTranslation = "cat\nDog"; - runTest(translator, input, expectedTranslation); - } - - /** - * Test whether translator throws an error. This should not be part of our - * regular testing, because we are limited to only 2MB of free translations - * ever. - * @param translator A BingTranslator - * @param input Text to translate - * @param expectedTranslation Not used unless you uncomment those lines. - */ - public void runTest(BingTranslator translator, String input, String expectedTranslation) { - String translation; - try { - translation = translator.translate(input); - } - catch (Throwable e) { - fail("Bing translation produced an exception: " + e.getMessage()); - return; - }; - - /* - //It's unrealistic to expect the same answer every time, but sometimes - //it's helpful to have this in your debug process. - System.out.println(translation); - assertEquals(expectedTranslation, translation); - */ +// BingTranslator translator = new BingTranslator(); +// String input = "gato"; +// String expectedTranslation = "cat"; +// runTest(translator, input, expectedTranslation); } + +// @Test +// public void testQuickStartSentence() throws Exception { +// BingTranslator translator = new BingTranslator(); +// String input = "Willkommen bei Microsoft Translator. Raten Sie mal, wie viele Sprachen ich spreche."; +// String expectedTranslation = "Welcome to Microsoft Translator. Guess how many languages I speak."; +// runTest(translator, input, expectedTranslation); +// } +// +// @Test +// public void testCharacterEscapes() throws Exception { +// BingTranslator translator = new BingTranslator(); +// String input = "\"gato\"";; +// String expectedTranslation = "Cat"; +// runTest(translator, input, expectedTranslation); +// } +// +// @Test +// public void testLineBreaks() throws Exception { +// BingTranslator translator = new BingTranslator(); +// String input = "gato\nperro";; +// String expectedTranslation = "cat\nDog"; +// runTest(translator, input, expectedTranslation); +// } +// +// /** +// * Test whether translator throws an error. This should not be part of our +// * regular testing, because we are limited to only 2MB of free translations +// * ever. +// * @param translator A BingTranslator +// * @param input Text to translate +// * @param expectedTranslation Not used unless you uncomment those lines. +// */ +// public void runTest(BingTranslator translator, String input, String expectedTranslation) { +// String translation; +// try { +// translation = translator.translate(input); +// } +// catch (Throwable e) { +// fail("Bing translation produced an exception: " + e.getMessage()); +// return; +// }; +// +// /* +// //It's unrealistic to expect the same answer every time, but sometimes +// //it's helpful to have this in your debug process. +// System.out.println(translation); +// assertEquals(expectedTranslation, translation); +// */ +// } } From 0c5ac83644ad09a3e72b1371dfb58c2a8ea2ddf4 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 30 May 2019 11:24:38 -0400 Subject: [PATCH 008/106] 5061 adjust BingTranslator to use user specified settings --- .../translators/BingTranslator.java | 20 +-- .../translators/BingTranslatorSettings.java | 33 +++- .../BingTranslatorSettingsPanel.form | 80 ++++++++- .../BingTranslatorSettingsPanel.java | 169 ++++++++++++++---- .../translators/Bundle.properties | 3 + .../translators/Bundle.properties-MERGED | 5 +- .../GoogleTranslatorSettingsPanel.form | 4 +- .../GoogleTranslatorSettingsPanel.java | 8 +- .../translators/LanguageWrapper.java | 32 +++- 9 files changed, 284 insertions(+), 70 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java index 1f790d14c3..4aaba09ee5 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java @@ -43,7 +43,7 @@ public class BingTranslator implements TextTranslator{ //In the String below, "en" is the target language. You can include multiple target //languages separated by commas. A full list of supported languages is here: //https://docs.microsoft.com/en-us/azure/cognitive-services/translator/language-support - private static final String URL = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=en"; + private static final String BASE_URL = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to="; private final BingTranslatorSettingsPanel settingsPanel; private final BingTranslatorSettings settings = new BingTranslatorSettings(); @@ -57,11 +57,11 @@ public class BingTranslator implements TextTranslator{ public BingTranslator(){ - settingsPanel = new BingTranslatorSettingsPanel(settings.getCredentials()); + settingsPanel = new BingTranslatorSettingsPanel(settings.getCredentials(), settings.getTargetLanguageCode()); } - static String getMicrosftTranlatorUrl(){ - return URL; + private String getTranlatorUrl(){ + return BASE_URL + settings.getTargetLanguageCode(); } /** @@ -84,7 +84,7 @@ public class BingTranslator implements TextTranslator{ RequestBody body = RequestBody.create(mediaType, bodyString); Request request = new Request.Builder() - .url(URL).post(body) + .url(getTranlatorUrl()).post(body) .addHeader("Ocp-Apim-Subscription-Key", settings.getCredentials()) .addHeader("Content-type", "application/json").build(); Response response = CLIENT.newCall(request).execute(); @@ -96,11 +96,10 @@ public class BingTranslator implements TextTranslator{ if (settings.getCredentials() == null || settings.getCredentials().isEmpty()) { throw new TranslationException("Bing Translator has not been configured, credentials need to be specified"); } - + String toTranslate = string.trim(); //Translates some text into English, without specifying the source langauge. // HTML files were producing lots of white space at the end - string = string.trim(); //Google Translate required us to replace (\r\n|\n) with
//but Bing Translator doesn not have that requirement. @@ -108,12 +107,12 @@ public class BingTranslator implements TextTranslator{ //The free account has a maximum file size. If you have a paid account, //you probably still want to limit file size to prevent accidentally //translating very large documents. - if (string.length() > MAX_STRING_LENGTH) { - string = string.substring(0, MAX_STRING_LENGTH); + if (toTranslate.length() > MAX_STRING_LENGTH) { + toTranslate = toTranslate.substring(0, MAX_STRING_LENGTH); } try { - String response = postTranslationRequest(string); + String response = postTranslationRequest(toTranslate); return parseJSONResponse(response); } catch (Throwable e) { throw new TranslationException(e.getMessage()); @@ -134,6 +133,7 @@ public class BingTranslator implements TextTranslator{ @Override public void saveSettings() { settings.setCredentials(settingsPanel.getCredentials()); + settings.setTargetLanguageCode(settingsPanel.getTargetLanguageCode()); } private String parseJSONResponse(String json_text) throws TranslationException { diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java index 6d6ef47b39..ca68adb77c 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java @@ -29,7 +29,10 @@ public final class BingTranslatorSettings { private static final String CREDENTIALS_KEY = "Credentials"; private static final String BING_TRANSLATE_NAME = "BingTranslate"; private static final String DEFAULT_CREDENTIALS = ""; + private static final String DEFAULT_TARGET_LANGUAGE = "en"; + private static final String TARGET_LANGUAGE_CODE_KEY = "TargetLanguageCode"; private String credentials; + private String targetLanguageCode; /** * Construct a new GoogleTranslatorSettingsObject @@ -56,7 +59,6 @@ public final class BingTranslatorSettings { credentials = creds; } - /** * Load the settings into memory from their on disk storage */ @@ -69,6 +71,34 @@ public final class BingTranslatorSettings { } else { credentials = DEFAULT_CREDENTIALS; } + if (ModuleSettings.settingExists(BING_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY)) { + targetLanguageCode = ModuleSettings.getConfigSetting(BING_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY); + } else { + targetLanguageCode = DEFAULT_TARGET_LANGUAGE; + } + } + + /** + * Get the target language code + * + * @return the code used to identify the target language + */ + String getTargetLanguageCode() { + return targetLanguageCode; + } + + /** + * Set the target language code. If a blank code is specified it sets the + * default code instead. + * + * @param code the target language code to set + */ + void setTargetLanguageCode(String code) { + if (StringUtils.isBlank(code)) { + targetLanguageCode = DEFAULT_TARGET_LANGUAGE; + } else { + targetLanguageCode = code; + } } /** @@ -76,5 +106,6 @@ public final class BingTranslatorSettings { */ void saveSettings() { ModuleSettings.setConfigSetting(BING_TRANSLATE_NAME, CREDENTIALS_KEY, credentials); + ModuleSettings.setConfigSetting(BING_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY, targetLanguageCode); } } diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form index 6acf950f40..3e7e151bbf 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form @@ -16,13 +16,9 @@ - + - - - - @@ -31,6 +27,24 @@ + + + + + + + + + + + + + + + + + + @@ -44,9 +58,20 @@ - + + + + + + + + + + + + - + @@ -81,5 +106,46 @@
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java index b49c3645b8..5488ca9c62 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.texttranslation.translators; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.google.gson.JsonObject; import com.squareup.okhttp.MediaType; @@ -27,7 +28,13 @@ import com.squareup.okhttp.Request; import com.squareup.okhttp.RequestBody; import com.squareup.okhttp.Response; import java.io.IOException; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import org.apache.commons.lang3.StringUtils; /** * Settings panel for the GoogleTranslator @@ -36,13 +43,70 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(BingTranslatorSettingsPanel.class.getName()); private static final long serialVersionUID = 1L; + private static final String GET_TARGET_LANGUAGES_URL = "https://api.cognitive.microsofttranslator.com/languages?api-version=3.0&scope=translation"; + private String targetLanguageCode = ""; /** * Creates new form GoogleTranslatorSettingsPanel */ - public BingTranslatorSettingsPanel(String credentials) { + public BingTranslatorSettingsPanel(String credentials, String code) { initComponents(); credentialsField.setText(credentials); + credentialsField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + firePropertyChange("SettingChanged", true, false); + } + + @Override + public void removeUpdate(DocumentEvent e) { + firePropertyChange("SettingChanged", true, false); + } + + @Override + public void changedUpdate(DocumentEvent e) { + firePropertyChange("SettingChanged", true, false); + } + + }); + targetLanguageCode = code; + populateComboBox(); + selectLanguageByCode(targetLanguageCode); + } + + private void populateComboBox() { + Request get_request = new Request.Builder() + .url(GET_TARGET_LANGUAGES_URL).build(); + try { + Response response = new OkHttpClient().newCall(get_request).execute(); + JsonParser parser = new JsonParser(); + String responseBody = response.body().string(); + JsonElement elementBody = parser.parse(responseBody); + JsonObject asObject = elementBody.getAsJsonObject(); + JsonElement translationElement = asObject.get("translation"); + JsonObject responses = translationElement.getAsJsonObject(); + for (Entry entry : responses.entrySet()) { + targetLanguageComboBox.addItem(new LanguageWrapper(entry.getKey(), entry.getValue().getAsJsonObject().get("name").getAsString())); + } + } catch (IOException | IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException ex) { + logger.log(Level.WARNING, "Unable to get list of target languages or parse the result that was received", ex); + } + + } + + /** + * Given a language code select the corresponding language in the combo box + * if it is present + * + * @param code language code such as "en" for English + */ + private void selectLanguageByCode(String code) { + for (int i = 0; i < targetLanguageComboBox.getModel().getSize(); i++) { + if (targetLanguageComboBox.getModel().getElementAt(i).getLanguageCode().equals(code)) { + targetLanguageComboBox.setSelectedIndex(i); + break; + } + } } /** @@ -58,6 +122,11 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { credentialsField = new javax.swing.JTextField(); warningLabel = new javax.swing.JLabel(); testButton = new javax.swing.JButton(); + jLabel1 = new javax.swing.JLabel(); + targetLanguageComboBox = new javax.swing.JComboBox<>(); + jLabel2 = new javax.swing.JLabel(); + jSpinner1 = new javax.swing.JSpinner(); + jLabel3 = new javax.swing.JLabel(); org.openide.awt.Mnemonics.setLocalizedText(credentialsLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.credentialsLabel.text")); // NOI18N @@ -71,6 +140,20 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { } }); + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.jLabel1.text")); // NOI18N + + targetLanguageComboBox.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(java.awt.event.ItemEvent evt) { + targetLanguageComboBoxSelected(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.jLabel2.text")); // NOI18N + + jSpinner1.setModel(new javax.swing.SpinnerNumberModel(5000, 5000, 500000, 5000)); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.jLabel3.text")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -78,16 +161,27 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addComponent(credentialsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 86, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(credentialsField, javax.swing.GroupLayout.DEFAULT_SIZE, 463, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(testButton) - .addGap(8, 8, 8)))) + .addGap(8, 8, 8)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 192, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addComponent(jLabel2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel3))) + .addGap(0, 0, Short.MAX_VALUE)))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -97,9 +191,18 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { .addComponent(credentialsLabel) .addComponent(credentialsField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(testButton)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel1) + .addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel2) + .addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel3)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(49, Short.MAX_VALUE)) + .addContainerGap()) ); }// //GEN-END:initComponents @@ -111,9 +214,22 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { } }//GEN-LAST:event_testButtonActionPerformed + private void targetLanguageComboBoxSelected(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_targetLanguageComboBoxSelected + String selectedCode = ((LanguageWrapper) targetLanguageComboBox.getSelectedItem()).getLanguageCode(); + if (!StringUtils.isBlank(selectedCode) && !selectedCode.equals(targetLanguageCode)) { + targetLanguageCode = selectedCode; + firePropertyChange("SettingChanged", true, false); + } + }//GEN-LAST:event_targetLanguageComboBoxSelected + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JTextField credentialsField; private javax.swing.JLabel credentialsLabel; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + private javax.swing.JSpinner jSpinner1; + private javax.swing.JComboBox targetLanguageComboBox; private javax.swing.JButton testButton; private javax.swing.JLabel warningLabel; // End of variables declaration//GEN-END:variables @@ -129,35 +245,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { * cancellation, a connectivity problem or timeout. */ private boolean testTranslationSetup() { - String testString = "forense"; - MediaType mediaType = MediaType.parse("application/json"); - - JsonArray jsonArray = new JsonArray(); - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("Text", testString); - jsonArray.add(jsonObject); - String bodyString = jsonArray.toString(); - - RequestBody body = RequestBody.create(mediaType, - bodyString); - Request request = new Request.Builder() - .url(BingTranslator.getMicrosftTranlatorUrl()).post(body) - .addHeader("Ocp-Apim-Subscription-Key", credentialsField.getText()) - .addHeader("Content-type", "application/json").build(); - try { - Response response = new OkHttpClient().newCall(request).execute(); - JsonParser parser = new JsonParser(); - JsonArray responses = parser.parse(response.body().string()).getAsJsonArray(); - //As far as I know, there's always exactly one item in the array. - JsonObject response0 = responses.get(0).getAsJsonObject(); - JsonArray translations = response0.getAsJsonArray("translations"); - JsonObject translation0 = translations.get(0).getAsJsonObject(); - translation0.get("text").getAsString(); - return true; - } catch (IOException | IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException e) { - return false; - } - + return true; } /** @@ -168,4 +256,13 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { String getCredentials() { return credentialsField.getText(); } + + /** + * Get the currently selected target language code + * + * @return the target language code of the language selected in the combobox + */ + String getTargetLanguageCode() { + return targetLanguageCode; + } } diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties index 024ddfadcd..888cb51018 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties @@ -3,3 +3,6 @@ GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials: GoogleTranslatorSettingsPanel.warningLabel.text= GoogleTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: BingTranslatorSettingsPanel.testButton.text=Test +BingTranslatorSettingsPanel.jLabel1.text=Target Language: +BingTranslatorSettingsPanel.jLabel2.text=Translation Size: +BingTranslatorSettingsPanel.jLabel3.text=characters diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED index bfe84cb856..2fb4ea5ef4 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED @@ -12,4 +12,7 @@ GoogleTranslatorSettingsPanel.fileChooser.confirmButton=Select GoogleTranslatorSettingsPanel.json.description=JSON Files GoogleTranslatorSettingsPanel.warningLabel.text= GoogleTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: -BingTranslatorSettingsPanel.jButton1.text=Test +BingTranslatorSettingsPanel.testButton.text=Test +BingTranslatorSettingsPanel.jLabel1.text=Target Language: +BingTranslatorSettingsPanel.jLabel2.text=Translation Size: +BingTranslatorSettingsPanel.jLabel3.text=characters diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form index 97a10b500f..1a78f115ac 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form @@ -98,7 +98,7 @@
- + @@ -119,4 +119,4 @@ - + \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java index fbb957413b..83ad5cf302 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java @@ -118,7 +118,7 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { targetLanguageComboBox.removeAllItems(); if (!listSupportedLanguages.isEmpty()) { listSupportedLanguages.forEach((lang) -> { - targetLanguageComboBox.addItem(new GoogleLanguageWrapper(lang)); + targetLanguageComboBox.addItem(new LanguageWrapper(lang)); }); selectLanguageByCode(targetLanguageCode); targetLanguageComboBox.addItemListener(listener); @@ -145,7 +145,7 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { */ private void selectLanguageByCode(String code) { for (int i = 0; i < targetLanguageComboBox.getModel().getSize(); i++) { - if (targetLanguageComboBox.getItemAt(i).getLanguage().getCode().equals(code)) { + if (targetLanguageComboBox.getItemAt(i).getLanguageCode().equals(code)) { targetLanguageComboBox.setSelectedIndex(i); return; } @@ -253,7 +253,7 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { private javax.swing.JButton browseButton; private javax.swing.JLabel credentialsLabel; private javax.swing.JTextField credentialsPathField; - private javax.swing.JComboBox targetLanguageComboBox; + private javax.swing.JComboBox targetLanguageComboBox; private javax.swing.JLabel targetLanguageLabel; private javax.swing.JLabel warningLabel; // End of variables declaration//GEN-END:variables @@ -284,7 +284,7 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { @Override public void itemStateChanged(java.awt.event.ItemEvent evt) { - String selectedCode = ((GoogleLanguageWrapper) targetLanguageComboBox.getSelectedItem()).getLanguage().getCode(); + String selectedCode = ((LanguageWrapper) targetLanguageComboBox.getSelectedItem()).getLanguageCode(); if (!StringUtils.isBlank(selectedCode) && !selectedCode.equals(targetLanguageCode)) { targetLanguageCode = selectedCode; populateTargetLanguageComboBox(); diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/LanguageWrapper.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/LanguageWrapper.java index c61ed1c948..3c3c048469 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/LanguageWrapper.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/LanguageWrapper.java @@ -21,19 +21,33 @@ package org.sleuthkit.autopsy.texttranslation.translators; import com.google.cloud.translate.Language; /** - * Wrapper for the Language class + * Wrapper for Language definitions used by translators */ -class GoogleLanguageWrapper { +class LanguageWrapper { - private final Language language; + private final String languageCode; + private final String languageDisplayName; /** - * Create a new GoogleLanguageWrapper + * Create a new LanguageWrapper to wrap the google language object * * @param lang the Language object to wrap */ - GoogleLanguageWrapper(Language lang) { - language = lang; + LanguageWrapper(Language language) { + languageCode = language.getCode(); + languageDisplayName = language.getName(); + } + + /** + * Create a new LanguageWrapper to wrap json elements that identify a + * language for microsofts translation service + * + * @param code the code which uniquely identifies a language + * @param name the name of the language + */ + LanguageWrapper(String code, String name) { + languageCode = code; + languageDisplayName = name; } /** @@ -41,14 +55,14 @@ class GoogleLanguageWrapper { * * @return the wrapped Language */ - Language getLanguage() { - return language; + String getLanguageCode() { + return languageCode; } @Override public String toString() { //toString overridden so that the jComboBox in the GoogleTranslatorSettingsPanel will display the name of the language - return language.getName(); + return languageDisplayName; } } From 90a002efee982071e665ac8156f44f1adaa088c7 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 30 May 2019 12:21:05 -0400 Subject: [PATCH 009/106] 5061 add test button to Bing translator settings panel --- .../translators/BingTranslator.java | 6 +- .../BingTranslatorSettingsPanel.form | 93 ++++++++++---- .../BingTranslatorSettingsPanel.java | 117 +++++++++++++----- .../translators/Bundle.properties | 9 +- .../translators/Bundle.properties-MERGED | 9 +- .../translators/LanguageWrapper.java | 2 +- 6 files changed, 175 insertions(+), 61 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java index 4aaba09ee5..1d27d8ce33 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java @@ -60,8 +60,8 @@ public class BingTranslator implements TextTranslator{ settingsPanel = new BingTranslatorSettingsPanel(settings.getCredentials(), settings.getTargetLanguageCode()); } - private String getTranlatorUrl(){ - return BASE_URL + settings.getTargetLanguageCode(); + static String getTranlatorUrl(String languageCode){ + return BASE_URL + languageCode; } /** @@ -84,7 +84,7 @@ public class BingTranslator implements TextTranslator{ RequestBody body = RequestBody.create(mediaType, bodyString); Request request = new Request.Builder() - .url(getTranlatorUrl()).post(body) + .url(getTranlatorUrl(settings.getTargetLanguageCode())).post(body) .addHeader("Ocp-Apim-Subscription-Key", settings.getCredentials()) .addHeader("Content-type", "application/json").build(); Response response = CLIENT.newCall(request).execute(); diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form index 3e7e151bbf..944cc3d124 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form @@ -23,24 +23,38 @@ - - - + - + - + - + + + + - - - + + + + + + + + + + + + + + + + @@ -56,20 +70,27 @@ - - + - - - + + + - + + + + + + + + + @@ -106,10 +127,10 @@ - + - + @@ -126,24 +147,52 @@ - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java index 5488ca9c62..4282ecbc63 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java @@ -44,6 +44,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(BingTranslatorSettingsPanel.class.getName()); private static final long serialVersionUID = 1L; private static final String GET_TARGET_LANGUAGES_URL = "https://api.cognitive.microsofttranslator.com/languages?api-version=3.0&scope=translation"; + private static final String DEFUALT_TEST_STRING = "traducción exitoso"; //spanish which should translate to something along the lines of translation successful private String targetLanguageCode = ""; /** @@ -51,6 +52,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { */ public BingTranslatorSettingsPanel(String credentials, String code) { initComponents(); + credentialsField.setText(credentials); credentialsField.getDocument().addDocumentListener(new DocumentListener() { @Override @@ -122,11 +124,15 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { credentialsField = new javax.swing.JTextField(); warningLabel = new javax.swing.JLabel(); testButton = new javax.swing.JButton(); - jLabel1 = new javax.swing.JLabel(); + targetLanguageLabel = new javax.swing.JLabel(); targetLanguageComboBox = new javax.swing.JComboBox<>(); - jLabel2 = new javax.swing.JLabel(); - jSpinner1 = new javax.swing.JSpinner(); - jLabel3 = new javax.swing.JLabel(); + translationSizeLabel = new javax.swing.JLabel(); + translationSizeSpinner = new javax.swing.JSpinner(); + unitsLabel = new javax.swing.JLabel(); + testUntranslatedTextField = new javax.swing.JTextField(); + untranslatedLabel = new javax.swing.JLabel(); + resultLabel = new javax.swing.JLabel(); + testResultValueLabel = new javax.swing.JLabel(); org.openide.awt.Mnemonics.setLocalizedText(credentialsLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.credentialsLabel.text")); // NOI18N @@ -140,7 +146,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { } }); - org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.jLabel1.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(targetLanguageLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.targetLanguageLabel.text")); // NOI18N targetLanguageComboBox.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { @@ -148,11 +154,19 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { } }); - org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.jLabel2.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(translationSizeLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.translationSizeLabel.text")); // NOI18N - jSpinner1.setModel(new javax.swing.SpinnerNumberModel(5000, 5000, 500000, 5000)); + translationSizeSpinner.setModel(new javax.swing.SpinnerNumberModel(5000, 5000, 500000, 5000)); - org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.jLabel3.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(unitsLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.unitsLabel.text")); // NOI18N + + testUntranslatedTextField.setText(DEFUALT_TEST_STRING); + + org.openide.awt.Mnemonics.setLocalizedText(untranslatedLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.untranslatedLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(resultLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.resultLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(testResultValueLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.testResultValueLabel.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); @@ -165,22 +179,32 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { .addComponent(credentialsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 86, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(credentialsField, javax.swing.GroupLayout.DEFAULT_SIZE, 463, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(testButton) - .addGap(8, 8, 8)) + .addGap(67, 67, 67)) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createSequentialGroup() - .addComponent(jLabel1) + .addComponent(targetLanguageLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 192, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createSequentialGroup() - .addComponent(jLabel2) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(translationSizeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(testButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel3))) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(translationSizeSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(unitsLabel)) + .addGroup(layout.createSequentialGroup() + .addComponent(untranslatedLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(resultLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(testResultValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))) .addGap(0, 0, Short.MAX_VALUE)))) ); layout.setVerticalGroup( @@ -189,18 +213,24 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(credentialsLabel) - .addComponent(credentialsField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(testButton)) + .addComponent(credentialsField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel1) + .addComponent(targetLanguageLabel) .addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel2) - .addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel3)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(translationSizeLabel) + .addComponent(translationSizeSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(unitsLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(testButton) + .addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(untranslatedLabel) + .addComponent(resultLabel) + .addComponent(testResultValueLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) ); @@ -225,12 +255,16 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JTextField credentialsField; private javax.swing.JLabel credentialsLabel; - private javax.swing.JLabel jLabel1; - private javax.swing.JLabel jLabel2; - private javax.swing.JLabel jLabel3; - private javax.swing.JSpinner jSpinner1; + private javax.swing.JLabel resultLabel; private javax.swing.JComboBox targetLanguageComboBox; + private javax.swing.JLabel targetLanguageLabel; private javax.swing.JButton testButton; + private javax.swing.JLabel testResultValueLabel; + private javax.swing.JTextField testUntranslatedTextField; + private javax.swing.JLabel translationSizeLabel; + private javax.swing.JSpinner translationSizeSpinner; + private javax.swing.JLabel unitsLabel; + private javax.swing.JLabel untranslatedLabel; private javax.swing.JLabel warningLabel; // End of variables declaration//GEN-END:variables /** @@ -245,7 +279,32 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { * cancellation, a connectivity problem or timeout. */ private boolean testTranslationSetup() { - return true; + MediaType mediaType = MediaType.parse("application/json"); + JsonArray jsonArray = new JsonArray(); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("Text", testUntranslatedTextField.getText()); + jsonArray.add(jsonObject); + String bodyString = jsonArray.toString(); + + RequestBody body = RequestBody.create(mediaType, + bodyString); + Request request = new Request.Builder() + .url(BingTranslator.getTranlatorUrl(targetLanguageCode)).post(body) + .addHeader("Ocp-Apim-Subscription-Key", credentialsField.getText()) + .addHeader("Content-type", "application/json").build(); + try { + Response response = new OkHttpClient().newCall(request).execute(); + JsonParser parser = new JsonParser(); + JsonArray responses = parser.parse(response.body().string()).getAsJsonArray(); + //As far as I know, there's always exactly one item in the array. + JsonObject response0 = responses.get(0).getAsJsonObject(); + JsonArray translations = response0.getAsJsonArray("translations"); + JsonObject translation0 = translations.get(0).getAsJsonObject(); + testResultValueLabel.setText(translation0.get("text").getAsString()); + return true; + } catch (IOException | IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException e) { + return false; + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties index 888cb51018..adf27cddfa 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties @@ -3,6 +3,9 @@ GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials: GoogleTranslatorSettingsPanel.warningLabel.text= GoogleTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: BingTranslatorSettingsPanel.testButton.text=Test -BingTranslatorSettingsPanel.jLabel1.text=Target Language: -BingTranslatorSettingsPanel.jLabel2.text=Translation Size: -BingTranslatorSettingsPanel.jLabel3.text=characters +BingTranslatorSettingsPanel.testResultValueLabel.text= +BingTranslatorSettingsPanel.resultLabel.text=Result: +BingTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: +BingTranslatorSettingsPanel.translationSizeLabel.text=Translation Size: +BingTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: +BingTranslatorSettingsPanel.unitsLabel.text=characters diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED index 2fb4ea5ef4..01c6123934 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED @@ -13,6 +13,9 @@ GoogleTranslatorSettingsPanel.json.description=JSON Files GoogleTranslatorSettingsPanel.warningLabel.text= GoogleTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: BingTranslatorSettingsPanel.testButton.text=Test -BingTranslatorSettingsPanel.jLabel1.text=Target Language: -BingTranslatorSettingsPanel.jLabel2.text=Translation Size: -BingTranslatorSettingsPanel.jLabel3.text=characters +BingTranslatorSettingsPanel.testResultValueLabel.text= +BingTranslatorSettingsPanel.resultLabel.text=Result: +BingTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: +BingTranslatorSettingsPanel.translationSizeLabel.text=Translation Size: +BingTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: +BingTranslatorSettingsPanel.unitsLabel.text=characters diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/LanguageWrapper.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/LanguageWrapper.java index 3c3c048469..2fa308291b 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/LanguageWrapper.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/LanguageWrapper.java @@ -61,7 +61,7 @@ class LanguageWrapper { @Override public String toString() { - //toString overridden so that the jComboBox in the GoogleTranslatorSettingsPanel will display the name of the language + //toString overridden so that the jComboBox in the TranslatorSettingsPanels will display the name of the language return languageDisplayName; } From d2bf1777f293b26d1fbde974274da0d1a7889e56 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 30 May 2019 12:35:24 -0400 Subject: [PATCH 010/106] 5061 reword credentials to be authentication key for bing translator --- .../translators/BingTranslator.java | 15 ++-- .../translators/BingTranslatorSettings.java | 32 +++++---- .../BingTranslatorSettingsPanel.form | 37 ++++++---- .../BingTranslatorSettingsPanel.java | 68 ++++++++++--------- .../translators/Bundle.properties | 3 +- 5 files changed, 86 insertions(+), 69 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java index 1d27d8ce33..db17236edc 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java @@ -46,18 +46,15 @@ public class BingTranslator implements TextTranslator{ private static final String BASE_URL = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to="; private final BingTranslatorSettingsPanel settingsPanel; private final BingTranslatorSettings settings = new BingTranslatorSettings(); - - // This sends messages to Microsoft. - private final OkHttpClient CLIENT = new OkHttpClient(); - + private final OkHttpClient CLIENT = new OkHttpClient(); //We might want to make this a configurable setting for anyone who has a //paid account that's willing to pay for long documents. private final int MAX_STRING_LENGTH = 5000; public BingTranslator(){ - settingsPanel = new BingTranslatorSettingsPanel(settings.getCredentials(), settings.getTargetLanguageCode()); + settingsPanel = new BingTranslatorSettingsPanel(settings.getAuthenticationKey(), settings.getTargetLanguageCode()); } static String getTranlatorUrl(String languageCode){ @@ -85,7 +82,7 @@ public class BingTranslator implements TextTranslator{ bodyString); Request request = new Request.Builder() .url(getTranlatorUrl(settings.getTargetLanguageCode())).post(body) - .addHeader("Ocp-Apim-Subscription-Key", settings.getCredentials()) + .addHeader("Ocp-Apim-Subscription-Key", settings.getAuthenticationKey()) .addHeader("Content-type", "application/json").build(); Response response = CLIENT.newCall(request).execute(); return response.body().string(); @@ -93,8 +90,8 @@ public class BingTranslator implements TextTranslator{ @Override public String translate(String string) throws TranslationException { - if (settings.getCredentials() == null || settings.getCredentials().isEmpty()) { - throw new TranslationException("Bing Translator has not been configured, credentials need to be specified"); + if (settings.getAuthenticationKey() == null || settings.getAuthenticationKey().isEmpty()) { + throw new TranslationException("Bing Translator has not been configured, authentication key needs to be specified"); } String toTranslate = string.trim(); //Translates some text into English, without specifying the source langauge. @@ -132,7 +129,7 @@ public class BingTranslator implements TextTranslator{ @Override public void saveSettings() { - settings.setCredentials(settingsPanel.getCredentials()); + settings.setAuthenticationKey(settingsPanel.getAuthenticationKey()); settings.setTargetLanguageCode(settingsPanel.getTargetLanguageCode()); } diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java index ca68adb77c..039c24c480 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java @@ -26,12 +26,12 @@ import org.sleuthkit.autopsy.coreutils.ModuleSettings; */ public final class BingTranslatorSettings { - private static final String CREDENTIALS_KEY = "Credentials"; + private static final String AUTHENTICATION_KEY = "Credentials"; private static final String BING_TRANSLATE_NAME = "BingTranslate"; - private static final String DEFAULT_CREDENTIALS = ""; + private static final String DEFAULT_AUTHENTICATION = ""; private static final String DEFAULT_TARGET_LANGUAGE = "en"; private static final String TARGET_LANGUAGE_CODE_KEY = "TargetLanguageCode"; - private String credentials; + private String authenticationKey; private String targetLanguageCode; /** @@ -42,21 +42,23 @@ public final class BingTranslatorSettings { } /** - * Get the path to the JSON credentials file + * Get the Authentication key to be used for the Microsoft translation + * service * - * @return the path to the credentials file + * @return the Authentication key for the service */ - String getCredentials() { - return credentials; + String getAuthenticationKey() { + return authenticationKey; } /** - * Set the path to the JSON credentials file + * Set the Authentication key to be used for the Microsoft translation + * service * - * @param path the path to the credentials file + * @param authKey the Authentication key for the service */ - void setCredentials(String creds) { - credentials = creds; + void setAuthenticationKey(String authKey) { + authenticationKey = authKey; } /** @@ -66,10 +68,10 @@ public final class BingTranslatorSettings { if (!ModuleSettings.configExists(BING_TRANSLATE_NAME)) { ModuleSettings.makeConfigFile(BING_TRANSLATE_NAME); } - if (ModuleSettings.settingExists(BING_TRANSLATE_NAME, CREDENTIALS_KEY)) { - credentials = ModuleSettings.getConfigSetting(BING_TRANSLATE_NAME, CREDENTIALS_KEY); + if (ModuleSettings.settingExists(BING_TRANSLATE_NAME, AUTHENTICATION_KEY)) { + authenticationKey = ModuleSettings.getConfigSetting(BING_TRANSLATE_NAME, AUTHENTICATION_KEY); } else { - credentials = DEFAULT_CREDENTIALS; + authenticationKey = DEFAULT_AUTHENTICATION; } if (ModuleSettings.settingExists(BING_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY)) { targetLanguageCode = ModuleSettings.getConfigSetting(BING_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY); @@ -105,7 +107,7 @@ public final class BingTranslatorSettings { * Save the setting from memory to their location on disk */ void saveSettings() { - ModuleSettings.setConfigSetting(BING_TRANSLATE_NAME, CREDENTIALS_KEY, credentials); + ModuleSettings.setConfigSetting(BING_TRANSLATE_NAME, AUTHENTICATION_KEY, authenticationKey); ModuleSettings.setConfigSetting(BING_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY, targetLanguageCode); } } diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form index 944cc3d124..5b275b52e3 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form @@ -19,31 +19,37 @@ - - - - - - - + + + + + + + + + + + + - + + @@ -57,7 +63,7 @@ - + @@ -68,8 +74,8 @@ - - + + @@ -98,14 +104,19 @@ - + - + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java index 4282ecbc63..b95887cea1 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java @@ -28,7 +28,6 @@ import com.squareup.okhttp.Request; import com.squareup.okhttp.RequestBody; import com.squareup.okhttp.Response; import java.io.IOException; -import java.util.Iterator; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; @@ -44,17 +43,17 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(BingTranslatorSettingsPanel.class.getName()); private static final long serialVersionUID = 1L; private static final String GET_TARGET_LANGUAGES_URL = "https://api.cognitive.microsofttranslator.com/languages?api-version=3.0&scope=translation"; - private static final String DEFUALT_TEST_STRING = "traducción exitoso"; //spanish which should translate to something along the lines of translation successful + private static final String DEFUALT_TEST_STRING = "traducción exitoso"; //spanish which should translate to something along the lines of "successful translation" private String targetLanguageCode = ""; /** * Creates new form GoogleTranslatorSettingsPanel */ - public BingTranslatorSettingsPanel(String credentials, String code) { + public BingTranslatorSettingsPanel(String authenticationKey, String code) { initComponents(); - - credentialsField.setText(credentials); - credentialsField.getDocument().addDocumentListener(new DocumentListener() { + + authenticationKeyField.setText(authenticationKey); + authenticationKeyField.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { firePropertyChange("SettingChanged", true, false); @@ -69,7 +68,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { public void changedUpdate(DocumentEvent e) { firePropertyChange("SettingChanged", true, false); } - + }); targetLanguageCode = code; populateComboBox(); @@ -87,9 +86,9 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { JsonObject asObject = elementBody.getAsJsonObject(); JsonElement translationElement = asObject.get("translation"); JsonObject responses = translationElement.getAsJsonObject(); - for (Entry entry : responses.entrySet()) { + responses.entrySet().forEach((entry) -> { targetLanguageComboBox.addItem(new LanguageWrapper(entry.getKey(), entry.getValue().getAsJsonObject().get("name").getAsString())); - } + }); } catch (IOException | IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException ex) { logger.log(Level.WARNING, "Unable to get list of target languages or parse the result that was received", ex); } @@ -120,8 +119,8 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { // //GEN-BEGIN:initComponents private void initComponents() { - credentialsLabel = new javax.swing.JLabel(); - credentialsField = new javax.swing.JTextField(); + authenticationKeyLabel = new javax.swing.JLabel(); + authenticationKeyField = new javax.swing.JTextField(); warningLabel = new javax.swing.JLabel(); testButton = new javax.swing.JButton(); targetLanguageLabel = new javax.swing.JLabel(); @@ -134,7 +133,9 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { resultLabel = new javax.swing.JLabel(); testResultValueLabel = new javax.swing.JLabel(); - org.openide.awt.Mnemonics.setLocalizedText(credentialsLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.credentialsLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(authenticationKeyLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.credentialsLabel.text")); // NOI18N + + authenticationKeyField.setToolTipText(org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.authenticationKeyField.toolTipText")); // NOI18N warningLabel.setForeground(new java.awt.Color(255, 0, 0)); org.openide.awt.Mnemonics.setLocalizedText(warningLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.warningLabel.text")); // NOI18N @@ -175,28 +176,32 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(credentialsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 86, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(credentialsField, javax.swing.GroupLayout.DEFAULT_SIZE, 463, Short.MAX_VALUE) - .addGap(67, 67, 67)) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createSequentialGroup() .addComponent(targetLanguageLabel) + .addGap(18, 18, 18) + .addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 192, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(authenticationKeyLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 192, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(authenticationKeyField, javax.swing.GroupLayout.PREFERRED_SIZE, 486, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 20, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(translationSizeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(testButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGap(25, 25, 25) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(translationSizeSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(unitsLabel)) + .addComponent(unitsLabel) + .addGap(0, 0, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addComponent(untranslatedLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -205,15 +210,15 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { .addComponent(resultLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(testResultValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))) - .addGap(0, 0, Short.MAX_VALUE)))) + .addContainerGap()))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(credentialsLabel) - .addComponent(credentialsField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(authenticationKeyLabel) + .addComponent(authenticationKeyField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(targetLanguageLabel) @@ -240,7 +245,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { if (testTranslationSetup()) { warningLabel.setText(""); } else { - warningLabel.setText("Invalid translation credentials"); + warningLabel.setText("Invalid translation authentication key"); } }//GEN-LAST:event_testButtonActionPerformed @@ -253,8 +258,8 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { }//GEN-LAST:event_targetLanguageComboBoxSelected // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JTextField credentialsField; - private javax.swing.JLabel credentialsLabel; + private javax.swing.JTextField authenticationKeyField; + private javax.swing.JLabel authenticationKeyLabel; private javax.swing.JLabel resultLabel; private javax.swing.JComboBox targetLanguageComboBox; private javax.swing.JLabel targetLanguageLabel; @@ -290,7 +295,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { bodyString); Request request = new Request.Builder() .url(BingTranslator.getTranlatorUrl(targetLanguageCode)).post(body) - .addHeader("Ocp-Apim-Subscription-Key", credentialsField.getText()) + .addHeader("Ocp-Apim-Subscription-Key", authenticationKeyField.getText()) .addHeader("Content-type", "application/json").build(); try { Response response = new OkHttpClient().newCall(request).execute(); @@ -308,12 +313,13 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { } /** - * Get the currently set path to the JSON credentials file + * Get the currently set authentication key to be used for the Microsoft + * translation service * - * @return the path to the credentials file specified in the textarea + * @return the authentication key specified in the textarea */ - String getCredentials() { - return credentialsField.getText(); + String getAuthenticationKey() { + return authenticationKeyField.getText(); } /** diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties index adf27cddfa..1d6a9318be 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties @@ -1,5 +1,5 @@ GoogleTranslatorSettingsPanel.browseButton.text=Browse -GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials: +GoogleTranslatorSettingsPanel.credentialsLabel.text=Authentication key: GoogleTranslatorSettingsPanel.warningLabel.text= GoogleTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: BingTranslatorSettingsPanel.testButton.text=Test @@ -9,3 +9,4 @@ BingTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: BingTranslatorSettingsPanel.translationSizeLabel.text=Translation Size: BingTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: BingTranslatorSettingsPanel.unitsLabel.text=characters +BingTranslatorSettingsPanel.authenticationKeyField.toolTipText=Enter the hash for the From fd399e4cfb7c080e358ea4dfd6e47ab3fb87f2d3 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 30 May 2019 13:04:32 -0400 Subject: [PATCH 011/106] 5061 clean up loading of settings --- .../texttranslation/translators/BingTranslatorSettings.java | 6 ++++-- .../texttranslation/translators/Bundle.properties-MERGED | 3 ++- .../translators/GoogleTranslatorSettings.java | 6 ++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java index 039c24c480..dcd54fd6f9 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java @@ -70,12 +70,14 @@ public final class BingTranslatorSettings { } if (ModuleSettings.settingExists(BING_TRANSLATE_NAME, AUTHENTICATION_KEY)) { authenticationKey = ModuleSettings.getConfigSetting(BING_TRANSLATE_NAME, AUTHENTICATION_KEY); - } else { + } + if (authenticationKey == null || StringUtils.isBlank(authenticationKey)) { authenticationKey = DEFAULT_AUTHENTICATION; } if (ModuleSettings.settingExists(BING_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY)) { targetLanguageCode = ModuleSettings.getConfigSetting(BING_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY); - } else { + } + if (targetLanguageCode == null || StringUtils.isBlank(targetLanguageCode)) { targetLanguageCode = DEFAULT_TARGET_LANGUAGE; } } diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED index 01c6123934..89f7d8b02a 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED @@ -1,7 +1,7 @@ BingTranslator.name.text=Bing Translator GoogleTranslator.name.text=Google Translate GoogleTranslatorSettingsPanel.browseButton.text=Browse -GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials: +GoogleTranslatorSettingsPanel.credentialsLabel.text=Authentication key: GoogleTranslatorSettingsPanel.errorMessage.fileNotFound=Credentials file not found, please set the location to be a valid JSON credentials file. GoogleTranslatorSettingsPanel.errorMessage.noFileSelected=A JSON file must be selected to provide your credentials for Google Translate. GoogleTranslatorSettingsPanel.errorMessage.unableToMakeCredentials=Unable to construct credentials object from credentials file, please set the location to be a valid JSON credentials file. @@ -19,3 +19,4 @@ BingTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: BingTranslatorSettingsPanel.translationSizeLabel.text=Translation Size: BingTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: BingTranslatorSettingsPanel.unitsLabel.text=characters +BingTranslatorSettingsPanel.authenticationKeyField.toolTipText=Enter the hash for the diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettings.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettings.java index 55771b8c7a..34ebfd67d4 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettings.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettings.java @@ -92,12 +92,14 @@ public final class GoogleTranslatorSettings { } if (ModuleSettings.settingExists(GOOGLE_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY)) { targetLanguageCode = ModuleSettings.getConfigSetting(GOOGLE_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY); - } else { + } + if (targetLanguageCode == null || StringUtils.isBlank(targetLanguageCode)) { targetLanguageCode = DEFAULT_TARGET_LANGUAGE; } if (ModuleSettings.settingExists(GOOGLE_TRANSLATE_NAME, CREDENTIAL_PATH_KEY)) { credentialPath = ModuleSettings.getConfigSetting(GOOGLE_TRANSLATE_NAME, CREDENTIAL_PATH_KEY); - } else { + } + if (credentialPath == null) { credentialPath = DEFAULT_CREDENTIAL_PATH; } } From e6670f34bab10ab51a7bbc82d53d35668d0c54fe Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 30 May 2019 13:46:15 -0400 Subject: [PATCH 012/106] 5061 test button added to google translate options panel --- .../translators/BingTranslator.java | 75 ++++++-------- .../BingTranslatorSettingsPanel.java | 2 +- .../translators/Bundle.properties | 6 +- .../translators/Bundle.properties-MERGED | 6 +- .../GoogleTranslatorSettingsPanel.form | 71 ++++++++++++-- .../GoogleTranslatorSettingsPanel.java | 97 +++++++++++++++---- 6 files changed, 184 insertions(+), 73 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java index db17236edc..78f1cf9afd 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java @@ -16,7 +16,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.sleuthkit.autopsy.texttranslation.translators; import com.google.gson.JsonArray; @@ -35,11 +34,12 @@ import org.sleuthkit.autopsy.texttranslation.TextTranslator; import org.sleuthkit.autopsy.texttranslation.TranslationException; /** - * Translates text by making HTTP requests to Bing Translator. - * This requires a valid subscription key for a Microsoft Azure account. + * Translates text by making HTTP requests to Bing Translator. This requires a + * valid subscription key for a Microsoft Azure account. */ @ServiceProvider(service = TextTranslator.class) -public class BingTranslator implements TextTranslator{ +public class BingTranslator implements TextTranslator { + //In the String below, "en" is the target language. You can include multiple target //languages separated by commas. A full list of supported languages is here: //https://docs.microsoft.com/en-us/azure/cognitive-services/translator/language-support @@ -47,43 +47,45 @@ public class BingTranslator implements TextTranslator{ private final BingTranslatorSettingsPanel settingsPanel; private final BingTranslatorSettings settings = new BingTranslatorSettings(); // This sends messages to Microsoft. - private final OkHttpClient CLIENT = new OkHttpClient(); + private final OkHttpClient CLIENT = new OkHttpClient(); //We might want to make this a configurable setting for anyone who has a //paid account that's willing to pay for long documents. private final int MAX_STRING_LENGTH = 5000; - - - public BingTranslator(){ + + public BingTranslator() { settingsPanel = new BingTranslatorSettingsPanel(settings.getAuthenticationKey(), settings.getTargetLanguageCode()); } - - static String getTranlatorUrl(String languageCode){ + + static String getTranlatorUrl(String languageCode) { return BASE_URL + languageCode; } - + /** * Converts an input test to the JSON format required by Bing Translator, * posts it to Microsoft, and returns the JSON text response. - * + * * @param string The input text to be translated. + * * @return The translation response as a JSON string - * @throws IOException if the request could not be executed due to cancellation, a connectivity problem or timeout. + * + * @throws IOException if the request could not be executed due to + * cancellation, a connectivity problem or timeout. */ public String postTranslationRequest(String string) throws IOException { MediaType mediaType = MediaType.parse("application/json"); - + JsonArray jsonArray = new JsonArray(); JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("Text", string); jsonArray.add(jsonObject); String bodyString = jsonArray.toString(); - + RequestBody body = RequestBody.create(mediaType, - bodyString); + bodyString); Request request = new Request.Builder() - .url(getTranlatorUrl(settings.getTargetLanguageCode())).post(body) - .addHeader("Ocp-Apim-Subscription-Key", settings.getAuthenticationKey()) - .addHeader("Content-type", "application/json").build(); + .url(getTranlatorUrl(settings.getTargetLanguageCode())).post(body) + .addHeader("Ocp-Apim-Subscription-Key", settings.getAuthenticationKey()) + .addHeader("Content-type", "application/json").build(); Response response = CLIENT.newCall(request).execute(); return response.body().string(); } @@ -95,27 +97,25 @@ public class BingTranslator implements TextTranslator{ } String toTranslate = string.trim(); //Translates some text into English, without specifying the source langauge. - + // HTML files were producing lots of white space at the end - //Google Translate required us to replace (\r\n|\n) with
//but Bing Translator doesn not have that requirement. - //The free account has a maximum file size. If you have a paid account, //you probably still want to limit file size to prevent accidentally //translating very large documents. if (toTranslate.length() > MAX_STRING_LENGTH) { toTranslate = toTranslate.substring(0, MAX_STRING_LENGTH); } - + try { String response = postTranslationRequest(toTranslate); return parseJSONResponse(response); } catch (Throwable e) { - throw new TranslationException(e.getMessage()); + throw new TranslationException(e.getMessage()); } } - + @Messages({"BingTranslator.name.text=Bing Translator"}) @Override public String getName() { @@ -131,26 +131,15 @@ public class BingTranslator implements TextTranslator{ public void saveSettings() { settings.setAuthenticationKey(settingsPanel.getAuthenticationKey()); settings.setTargetLanguageCode(settingsPanel.getTargetLanguageCode()); + settings.saveSettings(); } private String parseJSONResponse(String json_text) throws TranslationException { - /* Here is an example of the text we get from Bing when input is "gato", - the Spanish word for cat: - [ - { - "detectedLanguage": { - "language": "es", - "score": 1.0 - }, - "translations": [ - { - "text": "cat", - "to": "en" - } - ] - } - ] - */ + /* + * Here is an example of the text we get from Bing when input is "gato", + * the Spanish word for cat: [ { "detectedLanguage": { "language": "es", + * "score": 1.0 }, "translations": [ { "text": "cat", "to": "en" } ] } ] + */ JsonParser parser = new JsonParser(); try { JsonArray responses = parser.parse(json_text).getAsJsonArray(); @@ -164,4 +153,4 @@ public class BingTranslator implements TextTranslator{ throw new TranslationException("JSON text does not match Bing Translator scheme: " + e); } } -} \ No newline at end of file +} diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java index b95887cea1..054f65a2f5 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java @@ -51,7 +51,6 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { */ public BingTranslatorSettingsPanel(String authenticationKey, String code) { initComponents(); - authenticationKeyField.setText(authenticationKey); authenticationKeyField.getDocument().addDocumentListener(new DocumentListener() { @Override @@ -284,6 +283,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { * cancellation, a connectivity problem or timeout. */ private boolean testTranslationSetup() { + testResultValueLabel.setText(""); MediaType mediaType = MediaType.parse("application/json"); JsonArray jsonArray = new JsonArray(); JsonObject jsonObject = new JsonObject(); diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties index 1d6a9318be..8eebeeab58 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties @@ -1,5 +1,5 @@ GoogleTranslatorSettingsPanel.browseButton.text=Browse -GoogleTranslatorSettingsPanel.credentialsLabel.text=Authentication key: +GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials Path: GoogleTranslatorSettingsPanel.warningLabel.text= GoogleTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: BingTranslatorSettingsPanel.testButton.text=Test @@ -10,3 +10,7 @@ BingTranslatorSettingsPanel.translationSizeLabel.text=Translation Size: BingTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: BingTranslatorSettingsPanel.unitsLabel.text=characters BingTranslatorSettingsPanel.authenticationKeyField.toolTipText=Enter the hash for the +GoogleTranslatorSettingsPanel.testButton.text=Test +GoogleTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: +GoogleTranslatorSettingsPanel.resultLabel.text=Result: +GoogleTranslatorSettingsPanel.testResultValueLabel.text= diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED index 89f7d8b02a..fec94d3419 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED @@ -1,7 +1,7 @@ BingTranslator.name.text=Bing Translator GoogleTranslator.name.text=Google Translate GoogleTranslatorSettingsPanel.browseButton.text=Browse -GoogleTranslatorSettingsPanel.credentialsLabel.text=Authentication key: +GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials Path: GoogleTranslatorSettingsPanel.errorMessage.fileNotFound=Credentials file not found, please set the location to be a valid JSON credentials file. GoogleTranslatorSettingsPanel.errorMessage.noFileSelected=A JSON file must be selected to provide your credentials for Google Translate. GoogleTranslatorSettingsPanel.errorMessage.unableToMakeCredentials=Unable to construct credentials object from credentials file, please set the location to be a valid JSON credentials file. @@ -20,3 +20,7 @@ BingTranslatorSettingsPanel.translationSizeLabel.text=Translation Size: BingTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: BingTranslatorSettingsPanel.unitsLabel.text=characters BingTranslatorSettingsPanel.authenticationKeyField.toolTipText=Enter the hash for the +GoogleTranslatorSettingsPanel.testButton.text=Test +GoogleTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: +GoogleTranslatorSettingsPanel.resultLabel.text=Result: +GoogleTranslatorSettingsPanel.testResultValueLabel.text= diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form index 1a78f115ac..7d728fd011 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form @@ -16,13 +16,9 @@ - + - - - - @@ -31,7 +27,7 @@ - + @@ -42,6 +38,18 @@ + + + + + + + + + + + + @@ -60,9 +68,16 @@ - + + + + + + + + + -
@@ -118,5 +133,43 @@
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java index 83ad5cf302..8bccc01485 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java @@ -21,7 +21,9 @@ package org.sleuthkit.autopsy.texttranslation.translators; import com.google.auth.Credentials; import com.google.auth.oauth2.ServiceAccountCredentials; import com.google.cloud.translate.Language; +import com.google.cloud.translate.Translate; import com.google.cloud.translate.TranslateOptions; +import com.google.cloud.translate.Translation; import java.awt.event.ItemListener; import java.io.File; import java.io.FileInputStream; @@ -44,6 +46,7 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(GoogleTranslatorSettingsPanel.class.getName()); private static final String JSON_EXTENSION = "json"; + private static final String DEFUALT_TEST_STRING = "traducción exitoso"; //spanish which should translate to something along the lines of "successful translation" private static final long serialVersionUID = 1L; private final ItemListener listener = new ComboBoxSelectionListener(); private String targetLanguageCode = ""; @@ -60,16 +63,15 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { /** * Private method to make a temporary translation service given the current - * settings and use it to retrieve the available target languages for - * population of combobox with target language with unsaved settings. + * settings with unsaved settings. * - * @return A list of Languages + * @return A Translate object which is the translation service */ @Messages({"GoogleTranslatorSettingsPanel.errorMessage.fileNotFound=Credentials file not found, please set the location to be a valid JSON credentials file.", "GoogleTranslatorSettingsPanel.errorMessage.unableToReadCredentials=Unable to read credentials from credentials file, please set the location to be a valid JSON credentials file.", "GoogleTranslatorSettingsPanel.errorMessage.unableToMakeCredentials=Unable to construct credentials object from credentials file, please set the location to be a valid JSON credentials file.", "GoogleTranslatorSettingsPanel.errorMessage.unknownFailureGetting=Failure getting list of supported languages with current credentials file.",}) - private List getListOfTargetLanguages() { + private Translate getTemporaryTranslationService() { //This method also has the side effect of more or less validating the JSON file which was selected as it is necessary to get the list of target languages try { InputStream credentialStream; @@ -77,31 +79,31 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { credentialStream = new FileInputStream(credentialsPathField.getText()); } catch (FileNotFoundException ignored) { warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_fileNotFound()); - return new ArrayList<>(); + return null; } Credentials creds; try { creds = ServiceAccountCredentials.fromStream(credentialStream); } catch (IOException ignored) { warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_unableToMakeCredentials()); - return new ArrayList<>(); + return null; } if (creds == null) { warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_unableToReadCredentials()); logger.log(Level.WARNING, "Credentials were not successfully made, no translations will be available from the GoogleTranslator"); - return new ArrayList<>(); + return null; } else { TranslateOptions.Builder builder = TranslateOptions.newBuilder(); builder.setCredentials(creds); builder.setTargetLanguage(targetLanguageCode); //localize the list to the currently selected target language warningLabel.setText(""); //clear any previous warning text - return builder.build().getService().listSupportedLanguages(); + return builder.build().getService(); } } catch (Throwable throwable) { //Catching throwables because some of this Google Translate code throws throwables warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_unknownFailureGetting()); logger.log(Level.WARNING, "Throwable caught while getting list of supported languages", throwable); - return new ArrayList<>(); + return null; } } @@ -114,7 +116,13 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { targetLanguageComboBox.removeItemListener(listener); try { if (!StringUtils.isBlank(credentialsPathField.getText())) { - List listSupportedLanguages = getListOfTargetLanguages(); + List listSupportedLanguages; + Translate tempService = getTemporaryTranslationService(); + if (tempService != null) { + listSupportedLanguages = tempService.listSupportedLanguages(); + } else { + listSupportedLanguages = new ArrayList<>(); + } targetLanguageComboBox.removeAllItems(); if (!listSupportedLanguages.isEmpty()) { listSupportedLanguages.forEach((lang) -> { @@ -167,6 +175,11 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { targetLanguageComboBox = new javax.swing.JComboBox<>(); targetLanguageLabel = new javax.swing.JLabel(); warningLabel = new javax.swing.JLabel(); + testResultValueLabel = new javax.swing.JLabel(); + resultLabel = new javax.swing.JLabel(); + untranslatedLabel = new javax.swing.JLabel(); + testUntranslatedTextField = new javax.swing.JTextField(); + testButton = new javax.swing.JButton(); org.openide.awt.Mnemonics.setLocalizedText(credentialsLabel, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.credentialsLabel.text")); // NOI18N @@ -186,6 +199,21 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { warningLabel.setForeground(new java.awt.Color(255, 0, 0)); org.openide.awt.Mnemonics.setLocalizedText(warningLabel, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.warningLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(testResultValueLabel, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.testResultValueLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(resultLabel, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.resultLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(untranslatedLabel, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.untranslatedLabel.text")); // NOI18N + + testUntranslatedTextField.setText(DEFUALT_TEST_STRING); + + org.openide.awt.Mnemonics.setLocalizedText(testButton, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.testButton.text")); // NOI18N + testButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + testButtonActionPerformed(evt); + } + }); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -193,9 +221,6 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(credentialsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) @@ -203,13 +228,24 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(credentialsPathField, javax.swing.GroupLayout.DEFAULT_SIZE, 443, Short.MAX_VALUE) + .addComponent(credentialsPathField, javax.swing.GroupLayout.DEFAULT_SIZE, 451, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(browseButton) .addGap(14, 14, 14)) .addGroup(layout.createSequentialGroup() .addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 317, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, Short.MAX_VALUE)))))) + .addGap(0, 0, Short.MAX_VALUE)))) + .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(testButton, javax.swing.GroupLayout.PREFERRED_SIZE, 83, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(untranslatedLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(resultLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(testResultValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -223,9 +259,15 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(targetLanguageLabel) .addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(23, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 15, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(testButton) + .addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(untranslatedLabel) + .addComponent(resultLabel) + .addComponent(testResultValueLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE)) ); }// //GEN-END:initComponents @@ -249,12 +291,31 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { } }//GEN-LAST:event_browseButtonActionPerformed + private void testButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_testButtonActionPerformed + testResultValueLabel.setText(""); + Translate tempTranslate = getTemporaryTranslationService(); + if (tempTranslate != null) { + try { + Translation translation = tempTranslate.translate(testUntranslatedTextField.getText()); + testResultValueLabel.setText(translation.getTranslatedText()); + warningLabel.setText(""); + } catch (Exception ex) { + warningLabel.setText("Invalid translation credentials path"); + } + } + }//GEN-LAST:event_testButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton browseButton; private javax.swing.JLabel credentialsLabel; private javax.swing.JTextField credentialsPathField; + private javax.swing.JLabel resultLabel; private javax.swing.JComboBox targetLanguageComboBox; private javax.swing.JLabel targetLanguageLabel; + private javax.swing.JButton testButton; + private javax.swing.JLabel testResultValueLabel; + private javax.swing.JTextField testUntranslatedTextField; + private javax.swing.JLabel untranslatedLabel; private javax.swing.JLabel warningLabel; // End of variables declaration//GEN-END:variables From 670ef2cfb1d041c1654e84be1234bdae83893171 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 30 May 2019 16:05:30 -0400 Subject: [PATCH 013/106] 5061 fix label on bing translate options panel which was wrong --- .../BingTranslatorSettingsPanel.form | 18 +++++++++--------- .../BingTranslatorSettingsPanel.java | 11 +++++------ .../translators/Bundle.properties | 1 + .../translators/Bundle.properties-MERGED | 1 + 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form index 5b275b52e3..64227561b3 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form @@ -38,7 +38,7 @@ - + @@ -74,8 +74,8 @@ - + @@ -104,13 +104,6 @@ - - - - - - - @@ -207,5 +200,12 @@ + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java index 054f65a2f5..e477fbf662 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java @@ -28,7 +28,6 @@ import com.squareup.okhttp.Request; import com.squareup.okhttp.RequestBody; import com.squareup.okhttp.Response; import java.io.IOException; -import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.event.DocumentEvent; @@ -118,7 +117,6 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { // //GEN-BEGIN:initComponents private void initComponents() { - authenticationKeyLabel = new javax.swing.JLabel(); authenticationKeyField = new javax.swing.JTextField(); warningLabel = new javax.swing.JLabel(); testButton = new javax.swing.JButton(); @@ -131,8 +129,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { untranslatedLabel = new javax.swing.JLabel(); resultLabel = new javax.swing.JLabel(); testResultValueLabel = new javax.swing.JLabel(); - - org.openide.awt.Mnemonics.setLocalizedText(authenticationKeyLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.credentialsLabel.text")); // NOI18N + authenticationKeyLabel = new javax.swing.JLabel(); authenticationKeyField.setToolTipText(org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.authenticationKeyField.toolTipText")); // NOI18N @@ -168,6 +165,8 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(testResultValueLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.testResultValueLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(authenticationKeyLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.authenticationKeyLabel.text")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -216,8 +215,8 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(authenticationKeyLabel) - .addComponent(authenticationKeyField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(authenticationKeyField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(authenticationKeyLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(targetLanguageLabel) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties index 8eebeeab58..6ebd3002ef 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties @@ -14,3 +14,4 @@ GoogleTranslatorSettingsPanel.testButton.text=Test GoogleTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: GoogleTranslatorSettingsPanel.resultLabel.text=Result: GoogleTranslatorSettingsPanel.testResultValueLabel.text= +BingTranslatorSettingsPanel.authenticationKeyLabel.text=Authentication Key: diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED index fec94d3419..35e06bda99 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED @@ -24,3 +24,4 @@ GoogleTranslatorSettingsPanel.testButton.text=Test GoogleTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: GoogleTranslatorSettingsPanel.resultLabel.text=Result: GoogleTranslatorSettingsPanel.testResultValueLabel.text= +BingTranslatorSettingsPanel.authenticationKeyLabel.text=Authentication Key: From f05844a62cc162351ceebecd3abaaa8b9b3b5038 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Fri, 31 May 2019 14:57:31 -0400 Subject: [PATCH 014/106] 5123 remove all images and other uri elements from html by default --- .../autopsy/contentviewers/HtmlPanel.java | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java index f915b8d5a6..e4768d075e 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java @@ -18,6 +18,11 @@ */ package org.sleuthkit.autopsy.contentviewers; +import java.io.IOException; +import java.io.StringReader; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; @@ -25,8 +30,9 @@ 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.Node; +import net.htmlparser.jericho.Attribute; +import net.htmlparser.jericho.OutputDocument; +import net.htmlparser.jericho.Source; import org.openide.util.NbBundle.Messages; import org.w3c.dom.Document; import org.w3c.dom.NodeList; @@ -38,6 +44,7 @@ import org.w3c.dom.events.EventTarget; @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class HtmlPanel extends javax.swing.JPanel { + private static final Logger logger = Logger.getLogger(HtmlPanel.class.getName()); private static final long serialVersionUID = 1L; private static final String TEXT_TYPE = "text/plain"; private final JFXPanel jfxPanel = new JFXPanel(); @@ -99,12 +106,26 @@ final class HtmlPanel extends javax.swing.JPanel { * @return The cleansed HTML String */ private String cleanseHTML(String htmlInString) { - 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(); + String returnString = ""; + try { + Source source = new Source(new StringReader(htmlInString)); + OutputDocument document = new OutputDocument(source); + //remove background images + source.getAllTags().stream().filter((tag) -> (tag.toString().contains("background-image"))).forEachOrdered((tag) -> { + document.remove(tag); + }); + //remove images + source.getAllElements("img").forEach((element) -> { + document.remove(element.getAllTags()); + }); + //remove other URI elements such as input boxes + List attributesToRemove = source.getURIAttributes(); + document.remove(attributesToRemove); + returnString = document.toString(); + } catch (IOException ex) { + logger.log(Level.WARNING, "Unable to read html for cleaning out URI elements with Jericho", ex); + } + return returnString; } /** From 74ab8af6e049bda6d0a352488517f247c85bee09 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 3 Jun 2019 11:01:11 -0400 Subject: [PATCH 015/106] 5092 populate ingest status column datasource summary --- .../Bundle.properties-MERGED | 1 + .../datasourcesummary/DataSourceBrowser.java | 11 +++- .../datasourcesummary/DataSourceSummary.java | 56 +++++++++++++++++++ .../DataSourceSummaryNode.java | 4 ++ 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED index 508e04b76a..fe998b0e57 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED @@ -66,6 +66,7 @@ DataSourceSummaryDialog.window.title=Data Sources Summary DataSourceSummaryNode.column.dataSourceName.header=Data Source Name DataSourceSummaryNode.column.files.header=Files DataSourceSummaryNode.column.results.header=Results +DataSourceSummaryNode.column.status.header=Ingest Status DataSourceSummaryNode.column.tags.header=Tags DataSourceSummaryNode.column.type.header=Type DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java index e3b9fa0e93..e946317da8 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java @@ -39,6 +39,7 @@ import org.sleuthkit.autopsy.casemodule.datasourcesummary.DataSourceSummaryNode. import static javax.swing.SwingConstants.RIGHT; import javax.swing.table.TableColumn; import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.IngestJobInfo; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; @@ -50,9 +51,10 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(DataSourceBrowser.class.getName()); - private static final int COUNT_COLUMN_WIDTH = 25; - private static final int USAGE_COLUMN_WIDTH = 120; - private static final int DATA_SOURCE_COLUMN_WIDTH = 325; + private static final int COUNT_COLUMN_WIDTH = 20; + private static final int INGEST_STATUS_WIDTH = 50; + private static final int USAGE_COLUMN_WIDTH = 110; + private static final int DATA_SOURCE_COLUMN_WIDTH = 280; private final Outline outline; private final org.openide.explorer.view.OutlineView outlineView; private final ExplorerManager explorerManager; @@ -69,6 +71,7 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana outlineView = new org.openide.explorer.view.OutlineView(); this.setVisible(true); outlineView.setPropertyColumns( + Bundle.DataSourceSummaryNode_column_status_header(), Bundle.DataSourceSummaryNode_column_status_header(), Bundle.DataSourceSummaryNode_column_type_header(), Bundle.DataSourceSummaryNode_column_type_header(), Bundle.DataSourceSummaryNode_column_files_header(), Bundle.DataSourceSummaryNode_column_files_header(), Bundle.DataSourceSummaryNode_column_results_header(), Bundle.DataSourceSummaryNode_column_results_header(), @@ -90,6 +93,8 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana column.setPreferredWidth(COUNT_COLUMN_WIDTH); } else if (column.getHeaderValue().toString().equals(Bundle.DataSourceSummaryNode_column_type_header())) { column.setPreferredWidth(USAGE_COLUMN_WIDTH); + } else if (column.getHeaderValue().toString().equals(Bundle.DataSourceSummaryNode_column_status_header())) { + column.setPreferredWidth(INGEST_STATUS_WIDTH); } else { column.setPreferredWidth(DATA_SOURCE_COLUMN_WIDTH); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java index 1aa55d95b5..a04604b3cd 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java @@ -18,7 +18,17 @@ */ package org.sleuthkit.autopsy.casemodule.datasourcesummary; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.datamodel.CaseDbAccessManager; import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.IngestJobInfo; +import org.sleuthkit.datamodel.IngestJobInfo.IngestJobStatusType; +import org.sleuthkit.datamodel.TskCoreException; /** * Wrapper object for a DataSource and the information associated with it. @@ -27,6 +37,7 @@ import org.sleuthkit.datamodel.DataSource; class DataSourceSummary { private final DataSource dataSource; + private String status = ""; private final String type; private final long filesCount; private final long resultsCount; @@ -45,12 +56,23 @@ class DataSourceSummary { */ DataSourceSummary(DataSource dSource, String typeValue, Long numberOfFiles, Long numberOfResults, Long numberOfTags) { dataSource = dSource; + updateStatus(); type = typeValue == null ? "" : typeValue; filesCount = numberOfFiles == null ? 0 : numberOfFiles; resultsCount = numberOfResults == null ? 0 : numberOfResults; tagsCount = numberOfTags == null ? 0 : numberOfTags; } + void updateStatus() { + try { + IngestJobQueryCallback callback = new IngestJobQueryCallback(); + Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select("status_id FROM ingest_jobs WHERE obj_id=" + dataSource.getId(), callback); + status = callback.getStatus(); + } catch (NoCurrentCaseException | TskCoreException ex) { + + } + } + /** * Get the DataSource * @@ -87,6 +109,10 @@ class DataSourceSummary { return resultsCount; } + String getIngestStatus(){ + return status; + } + /** * Get the number of tagged content objects in this DataSource * @@ -95,4 +121,34 @@ class DataSourceSummary { long getTagsCount() { return tagsCount; } + + class IngestJobQueryCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + + IngestJobStatusType jobStatus = null; + + @Override + public void process(ResultSet rs) { + try { + while (rs.next()) { + IngestJobStatusType currentStatus = IngestJobStatusType.fromID(rs.getInt("status_id")); + if (currentStatus == IngestJobStatusType.COMPLETED) { + jobStatus = currentStatus; + } else if (currentStatus == IngestJobStatusType.STARTED) { + jobStatus = currentStatus; + return; + } + } + } catch (SQLException ex) { + System.out.println("EEEP"); + } + } + + String getStatus() { + if (jobStatus == null) { + return ""; + } else { + return jobStatus.getDisplayName(); + } + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java index 8323061a66..c9aef8c644 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java @@ -109,6 +109,7 @@ final class DataSourceSummaryNode extends AbstractNode { static final class DataSourceSummaryEntryNode extends AbstractNode { private final DataSource dataSource; + private final String status; private final String type; private final long filesCount; private final long resultsCount; @@ -124,6 +125,7 @@ final class DataSourceSummaryNode extends AbstractNode { DataSourceSummaryEntryNode(DataSourceSummary dataSourceSummary) { super(Children.LEAF); dataSource = dataSourceSummary.getDataSource(); + status = dataSourceSummary.getIngestStatus(); type = dataSourceSummary.getType(); filesCount = dataSourceSummary.getFilesCount(); resultsCount = dataSourceSummary.getResultsCount(); @@ -143,6 +145,7 @@ final class DataSourceSummaryNode extends AbstractNode { } @Messages({"DataSourceSummaryNode.column.dataSourceName.header=Data Source Name", + "DataSourceSummaryNode.column.status.header=Ingest Status", "DataSourceSummaryNode.column.type.header=Type", "DataSourceSummaryNode.column.files.header=Files", "DataSourceSummaryNode.column.results.header=Results", @@ -157,6 +160,7 @@ final class DataSourceSummaryNode extends AbstractNode { } sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_dataSourceName_header(), Bundle.DataSourceSummaryNode_column_dataSourceName_header(), Bundle.DataSourceSummaryNode_column_dataSourceName_header(), dataSource)); + sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_status_header(), Bundle.DataSourceSummaryNode_column_status_header(), Bundle.DataSourceSummaryNode_column_status_header(), status)); sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_type_header(), Bundle.DataSourceSummaryNode_column_type_header(), Bundle.DataSourceSummaryNode_column_type_header(), type)); sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_files_header(), Bundle.DataSourceSummaryNode_column_files_header(), Bundle.DataSourceSummaryNode_column_files_header(), From 1629706d4454d590258d07623e99715aae0fe55e Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 3 Jun 2019 11:22:10 -0400 Subject: [PATCH 016/106] Fix for exception when attempting to split empty keys. --- .../org/sleuthkit/autopsy/datamodel/BaseChildFactory.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java index 4a1147494a..9727d83e39 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java @@ -286,7 +286,12 @@ public abstract class BaseChildFactory extends ChildFactory.D * 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 (keys.isEmpty()) { + pages.clear(); + } else { + pages = Lists.partition(keys, pageSize > 0 ? pageSize : keys.size()); + } + if (pages.size() != oldPageCount) { try { // Number of pages has changed so we need to send out a notification. From 92e0d6073a81fa066c5521289e3540daa8555cef Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 3 Jun 2019 12:51:02 -0400 Subject: [PATCH 017/106] Turn result paging on by default. --- Core/src/org/sleuthkit/autopsy/core/UserPreferences.java | 4 ++-- .../org/sleuthkit/autopsy/corecomponents/Bundle.properties | 2 +- .../sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java index d6f68086c4..c18d8b0f96 100644 --- a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java +++ b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java @@ -504,10 +504,10 @@ public final class UserPreferences { /** * Get the maximum number of results to display in a result table. * - * @return Saved value or default (0) which indicates no max. + * @return Saved value or default (10,000). */ public static int getResultsTablePageSize() { - return preferences.getInt(RESULTS_TABLE_PAGE_SIZE, 0); + return preferences.getInt(RESULTS_TABLE_PAGE_SIZE, 10_000); } /** diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index ac5bccebc8..7eb261880f 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -216,4 +216,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 +ViewPreferencesPanel.maxResultsLabel.toolTipText=\nSetting this value to 0 will display all results in the results table.\n
Note that setting this value to 0 may result in poor UI responsiveness when there are large numbers of results.\n diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED index 2c0af06f50..cd75ae919e 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED @@ -269,4 +269,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 +ViewPreferencesPanel.maxResultsLabel.toolTipText=\nSetting this value to 0 will display all results in the results table.\n
Note that setting this value to 0 may result in poor UI responsiveness when there are large numbers of results.\n From b9ef321b1e96047407a177dc18ec6fb456d89aa4 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 3 Jun 2019 13:23:48 -0400 Subject: [PATCH 018/106] 5123 remove frames, iframes, audio, video, and other tags --- .../autopsy/contentviewers/HtmlPanel.java | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java index e4768d075e..b747e661e7 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java @@ -99,7 +99,7 @@ final class HtmlPanel extends javax.swing.JPanel { } /** - * Cleans out input HTML string + * Cleans out input HTML string so it will not access resources over the internet * * @param htmlInString The HTML string to cleanse * @@ -118,6 +118,42 @@ final class HtmlPanel extends javax.swing.JPanel { source.getAllElements("img").forEach((element) -> { document.remove(element.getAllTags()); }); + //remove frames + source.getAllElements("frame").forEach((element) -> { + document.remove(element.getAllTags()); + }); + //remove iframes + source.getAllElements("iframe").forEach((element) -> { + document.remove(element.getAllTags()); + }); + //remove pictures + source.getAllElements("picture").forEach((element) -> { + document.remove(element.getAllTags()); + }); + //remove svg + source.getAllElements("svg").forEach((element) -> { + document.remove(element.getAllTags()); + }); + //remove audio + source.getAllElements("audio").forEach((element) -> { + document.remove(element.getAllTags()); + }); + //remove video + source.getAllElements("video").forEach((element) -> { + document.remove(element.getAllTags()); + }); + //remove tracks + source.getAllElements("track").forEach((element) -> { + document.remove(element.getAllTags()); + }); + //remove embeded external elements + source.getAllElements("embed").forEach((element) -> { + document.remove(element.getAllTags()); + }); + //remove linked elements + source.getAllElements("link").forEach((element) -> { + document.remove(element.getAllTags()); + }); //remove other URI elements such as input boxes List attributesToRemove = source.getURIAttributes(); document.remove(attributesToRemove); From 51e12b2eaf88e9aaf0c6dbf499de89ca2db0e82f Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 3 Jun 2019 13:32:26 -0400 Subject: [PATCH 019/106] 5123 change text of Show images to say Download instead of show --- Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties | 2 +- Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form | 2 +- Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index 652f8781ba..fd3bbc833f 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -83,7 +83,7 @@ MediaViewImagePanel.zoomResetButton.text=Reset MediaViewImagePanel.zoomTextField.text= MediaViewImagePanel.rotationTextField.text= MediaViewImagePanel.rotateLeftButton.toolTipText= -HtmlPanel.showImagesToggleButton.text=Show Images +HtmlPanel.showImagesToggleButton.text=Download Images MediaPlayerPanel.audioSlider.toolTipText= MediaPlayerPanel.VolumeIcon.text=\ \ \ \ \ Volume MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00 diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form index 97b3c67f72..407f0e6ec7 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form @@ -18,7 +18,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java index b747e661e7..250f2ba3cd 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java @@ -221,7 +221,7 @@ final class HtmlPanel extends javax.swing.JPanel { layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(showImagesToggleButton) - .addGap(0, 95, Short.MAX_VALUE)) + .addGap(0, 75, Short.MAX_VALUE)) .addComponent(htmlJPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( From 3c03ee5ce3659422acbc3bf91cffca0c03200949 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 3 Jun 2019 13:36:02 -0400 Subject: [PATCH 020/106] 5123 update merged properties file --- .../sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index 236fddfada..7600a880a2 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -145,7 +145,7 @@ MediaViewImagePanel.zoomResetButton.text=Reset MediaViewImagePanel.zoomTextField.text= MediaViewImagePanel.rotationTextField.text= MediaViewImagePanel.rotateLeftButton.toolTipText= -HtmlPanel.showImagesToggleButton.text=Show Images +HtmlPanel.showImagesToggleButton.text=Download Images MediaPlayerPanel.audioSlider.toolTipText= MediaPlayerPanel.VolumeIcon.text=\ \ \ \ \ Volume MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00 From 90898578ab9045ebdd527dff222d519d321b9058 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Mon, 3 Jun 2019 13:36:29 -0400 Subject: [PATCH 021/106] Renamed classes, cleaned up code, added comments --- .../casemodule/services/TagsManager.java | 24 ++ .../ApplicationTagsManager.java | 67 ---- .../ContentViewerTagManager.java | 232 ++++++++++++ .../autopsy/contentviewers/Bundle.properties | 2 +- .../contentviewers/Bundle.properties-MERGED | 2 +- .../contentviewers/MediaViewImagePanel.form | 42 ++- .../contentviewers/MediaViewImagePanel.java | 355 ++++++++++++++---- .../imagetagging/FocusChangeEvent.java | 48 --- .../imagetagging/FocusChangeListener.java | 18 - .../contentviewers/imagetagging/ImageTag.java | 343 +++++++++++++++++ ...ControlType.java => ImageTagControls.java} | 7 +- .../imagetagging/ImageTagCreator.java | 95 +++-- .../imagetagging/ImageTagRegion.java | 82 ++++ .../imagetagging/ImageTagsGroup.java | 124 ++++++ .../imagetagging/StoredTag.java | 269 ------------- .../imagetagging/StoredTagEvent.java | 40 -- .../imagetagging/StoredTagListener.java | 33 -- .../imagetagging/TopLevelTagsGroup.java | 103 ----- 18 files changed, 1169 insertions(+), 717 deletions(-) delete mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ApplicationTagsManager.java create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java delete mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeEvent.java delete mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeListener.java create mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java rename Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/{ControlType.java => ImageTagControls.java} (84%) create mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagRegion.java create mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java delete mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTag.java delete mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagEvent.java delete mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagListener.java delete mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/TopLevelTagsGroup.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java index 896298c2bd..e328b572de 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.casemodule.services; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -30,9 +31,11 @@ import java.util.logging.Level; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifactTag; +import org.sleuthkit.datamodel.CaseDbAccessManager; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.SleuthkitCase; @@ -48,6 +51,27 @@ public class TagsManager implements Closeable { private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName()); private final SleuthkitCase caseDb; + + static { + //Create the contentviewer tags table (beta) if the current case does not + //have the table present + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), evt -> { + if (evt.getNewValue() != null) { + Case currentCase = (Case) evt.getNewValue(); + try { + CaseDbAccessManager caseDb = currentCase.getSleuthkitCase().getCaseDbAccessManager(); + //Create our custom application tags table, if need be. + if (!caseDb.tableExists(ContentViewerTagManager.TABLE_NAME)) { + caseDb.createTable(ContentViewerTagManager.TABLE_NAME, ContentViewerTagManager.TABLE_SCHEMA); + } + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, + String.format("Unable to create the %s table for image tag storage.", + ContentViewerTagManager.TABLE_NAME), ex); + } + } + }); + } /** * Tests whether or not a given tag display name contains an illegal diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ApplicationTagsManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ApplicationTagsManager.java deleted file mode 100755 index 65756d16c8..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ApplicationTagsManager.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.casemodule.services.applicationtags; - -import java.util.EnumSet; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.datamodel.CaseDbAccessManager; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * A per case Autopsy service that manages the addition of application content - * tags to the case database. - */ -public final class ApplicationTagsManager { - - private static CaseDbAccessManager dbManager; - - private static final String TABLE_NAME = "beta_tag_app_data"; - private static final String TABLE_SCHEMA = "(app_data_id INTEGER PRIMARY KEY, " - + "content_tag_id INTEGER NOT NULL, app_data TEXT NOT NULL," - + "FOREIGN KEY(content_tag_id) REFERENCES content_tags(tag_id))"; - - private final String INSERT_OR_REPLACE_TAG_DATA = "(content_tag_id, app_data) VALUES (?, ?)"; - private final String SELECT_TAG_DATA = "* FROM " + TABLE_NAME + " WHERE content_tag_id = ?"; - private final String DELETE_TAG_DATA = "WHERE app_data_id = ?"; - - static { - Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), evt -> { - if(evt.getNewValue() != null) { - Case currentCase = (Case) evt.getNewValue(); - try { - CaseDbAccessManager caseDb = currentCase.getSleuthkitCase().getCaseDbAccessManager(); - //Create our custom application tags table, if need be. - if (!caseDb.tableExists(TABLE_NAME)) { - caseDb.createTable(TABLE_NAME, TABLE_SCHEMA); - } - - dbManager = caseDb; - } catch (TskCoreException ex) { - //Log - } - } - }); - } - - //public createTag - //public updateTag - //public getTag - //public deleteTag - -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java new file mode 100755 index 0000000000..33b50bab07 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java @@ -0,0 +1,232 @@ +/* + * 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.casemodule.services.applicationtags; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * A per case Autopsy service that manages the addition of content viewer tags + * to the case database. This manager is also responsible for serializing and + * deserializing instances of your tag data objects for persistence and retrieval. + */ +public class ContentViewerTagManager { + + //Used to convert Java beans into the physical representation that will be stored + //in the database (for now, JSON was chosen). + private static final ObjectMapper SERIALIZER = new ObjectMapper(); + + public static final String TABLE_NAME = "beta_tag_app_data"; + public static final String TABLE_SCHEMA = "(app_data_id INTEGER PRIMARY KEY, " + + "content_tag_id INTEGER NOT NULL, app_data TEXT NOT NULL, " + + "FOREIGN KEY(content_tag_id) REFERENCES content_tags(tag_id))"; + + private static final String INSERT_TAG_DATA = "(content_tag_id, app_data) VALUES (%d, '%s')"; + private static final String UPDATE_TAG_DATA = "SET content_tag_id = %d, app_data = '%s' WHERE app_data_id = %d"; + private static final String SELECT_TAG_DATA = "* FROM " + TABLE_NAME + " WHERE content_tag_id = %d"; + private static final String DELETE_TAG_DATA = "WHERE app_data_id = %d"; + + /** + * Creates and saves a new ContentViewerTag in the case database. The + * generic tag data instance T will be automatically serialized into a + * storable format and persisted. + * + * @param Generic java bean class type that will be serialized into a + * storable format for persistence + * @param contentTag ContentTag that this ContentViewerTag is associated + * with (1:1) + * @param tagDataBean Data instance that contains the tag information to be + * persisted. + * @return An instance of a ContentViewerTag of type T, which contains all + * the stored information. + * + * @throws SerializationException Thrown if the tag data bean instance T + * could not be serialized into a storable format for persistence in the + * case database. + * @throws TskCoreException Thrown if this operation did not successfully + * persist in the case database. + * @throws NoCurrentCaseException Thrown if invocation of this method occurs + * when no case is open. + */ + public static ContentViewerTag saveTag(ContentTag contentTag, T tagDataBean) throws SerializationException, TskCoreException, NoCurrentCaseException { + try { + long contentTagId = contentTag.getId(); + String serialAppData = SERIALIZER.writeValueAsString(tagDataBean); + String insertTemplateInstance = String.format(INSERT_TAG_DATA, + contentTagId, serialAppData); + long insertId = Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().insert(TABLE_NAME, insertTemplateInstance); + return new ContentViewerTag<>(insertId, contentTag, tagDataBean); + } catch (JsonProcessingException ex) { + throw new SerializationException("Unable to convert object instance into a storable format", ex); + } + } + + /** + * Updates the ContentViewerTag instance with the new tag data T and + * persists the changes to the case database. + * + * @param Generic java bean class type that will be serialized into a + * storable format for persistence + * @param oldTag ContentViewerTag instance to be updated + * @param tagDataBean Data instance that contains the updated information to + * be persisted. + * + * @throws SerializationException Thrown if the tag data bean instance T + * could not be serialized into a storable format for persistence in the + * case database. + * @throws TskCoreException Thrown if this operation did not successfully + * persist in the case database. + * @throws NoCurrentCaseException Thrown if invocation of this method occurs + * when no case is open. + */ + public static ContentViewerTag updateTag(ContentViewerTag oldTag, T tagDataBean) + throws SerializationException, TskCoreException, NoCurrentCaseException { + try { + String serialAppData = SERIALIZER.writeValueAsString(tagDataBean); + String updateTemplateInstance = String.format(UPDATE_TAG_DATA, + oldTag.getContentTag().getId(), serialAppData, oldTag.getId()); + Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager() + .update(TABLE_NAME, updateTemplateInstance); + return new ContentViewerTag<>(oldTag.getId(), oldTag.getContentTag(), tagDataBean); + } catch (JsonProcessingException ex) { + throw new SerializationException("Unable to convert object instance into a storable format", ex); + } + } + + /** + * Retrieves a ContentViewerTag instance that is associated with the + * specified ContentTag. The Java bean class that represents the technical + * details of the tag should be passed so that automatic binding can take + * place. + * + * @param Java bean class type that will be instantiated and filled in + * with data. + * @param contentTag ContentTag that this ContentViewerTag is associated + * with (1:1) + * @param clazz Java bean class that will be instantiated and filled in with + * data. + * @return ContentViewerTag with an instance of T as a member variable or + * null if the content tag does not have an associated ContentViewerTag of + * type T. + * + * @throws TskCoreException Thrown if this operation did not successfully + * persist in the case database. + * @throws NoCurrentCaseException Thrown if invocation of this method occurs + * when no case is open. + */ + public static ContentViewerTag getTag(ContentTag contentTag, Class clazz) throws TskCoreException, NoCurrentCaseException { + try { + String selectTemplateInstance = String.format(SELECT_TAG_DATA, contentTag.getId()); + final ArrayList result = new ArrayList<>(); + Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager() + .select(selectTemplateInstance, (ResultSet rs) -> { + try { + if (rs.next()) { + long tagId = rs.getLong(1); + String appDetails = rs.getString(3); + try { + T instance = SERIALIZER.readValue(appDetails, clazz); + result.add(new ContentViewerTag<>(tagId, contentTag, instance)); + } catch (IOException ex) { + //Databind for type T failed. Not a system error + //but rather a logic error on the part of the caller. + result.add(null); + } + } + } catch (SQLException ex) { + throw new RuntimeException(ex); + } + }); + return result.get(0); + } catch (RuntimeException ex) { + throw new TskCoreException("Unable to select tags from db", (Exception) ex.getCause()); + } + } + + /** + * Deletes the content viewer tag with the specified id. + * + * @param contentViewerTag ContentViewerTag to delete + * @throws TskCoreException Thrown if this operation did not successfully + * persist in the case database. + * @throws NoCurrentCaseException Thrown if invocation of this method occurs + * when no case is open. + */ + public static void deleteTag(ContentViewerTag contentViewerTag) throws TskCoreException, NoCurrentCaseException { + String deleteTemplateInstance = String.format(DELETE_TAG_DATA, contentViewerTag.getId()); + Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager() + .delete(TABLE_NAME, deleteTemplateInstance); + } + + /** + * This class represents a stored tag in the case database. It is a wrapper + * for the tag id, the attached Content tag object, and the Java bean + * instance that describes the technical details for reconstructing the tag. + * + * @param Java bean class type that will be instantiated and filled in + * with data. + */ + public static class ContentViewerTag { + + private final long id; + private final ContentTag contentTag; + private final T details; + + private ContentViewerTag(long id, ContentTag contentTag, T details) { + this.id = id; + this.contentTag = contentTag; + this.details = details; + } + + public long getId() { + return id; + } + + public ContentTag getContentTag() { + return contentTag; + } + + public T getDetails() { + return details; + } + } + + /** + * System exception thrown in the event that class instance T could not be + * properly serialized. + */ + public static class SerializationException extends Exception { + + public SerializationException(String message, Exception source) { + super(message, source); + } + } + + //Prevent this class from being instantiated. + private ContentViewerTagManager() { + } +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index 631bb91273..a8b66e1c31 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -92,4 +92,4 @@ MediaPlayerPanel.infoLabel.text=No Errors MediaViewImagePanel.deleteTagButton.text=Delete Tag MediaViewImagePanel.createTagButton.text=Create Tag MediaViewImagePanel.jMenu1.text=jMenu1 -MediaViewImagePanel.editTagButton.text=Edit Tag Info +MediaViewImagePanel.showTagsButton.text=Hide Tags diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index b18b79580c..44cae34fd9 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -154,7 +154,7 @@ MediaPlayerPanel.infoLabel.text=No Errors MediaViewImagePanel.deleteTagButton.text=Delete Tag MediaViewImagePanel.createTagButton.text=Create Tag MediaViewImagePanel.jMenu1.text=jMenu1 -MediaViewImagePanel.editTagButton.text=Edit Tag Info +MediaViewImagePanel.showTagsButton.text=Hide Tags # {0} - tableName SQLiteViewer.readTable.errorText=Error getting rows for table: {0} # {0} - tableName diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form index ccfe83b2cc..447957d66c 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form @@ -225,10 +225,18 @@
- - + + + + + + + + + + @@ -245,21 +253,6 @@ - - - - - - - - - - - - - - - @@ -274,6 +267,21 @@ + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index b6ec14fba1..5d6fa7e193 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -18,18 +18,16 @@ */ package org.sleuthkit.autopsy.contentviewers; -import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagCreator; -import org.sleuthkit.autopsy.contentviewers.imagetagging.StoredTag; -import org.sleuthkit.autopsy.contentviewers.imagetagging.StoredTagListener; -import org.sleuthkit.autopsy.contentviewers.imagetagging.TopLevelTagsGroup; -import org.sleuthkit.autopsy.contentviewers.imagetagging.StoredTagEvent; import java.awt.EventQueue; import java.awt.event.ActionEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import static java.util.Objects.nonNull; import java.util.SortedSet; import java.util.concurrent.ExecutionException; +import java.util.logging.Level; import java.util.stream.Collectors; import javafx.application.Platform; import javafx.concurrent.Task; @@ -37,7 +35,7 @@ import javafx.embed.swing.JFXPanel; import javafx.geometry.Pos; import javafx.geometry.Rectangle2D; import javafx.scene.Cursor; -import javafx.scene.Node; +import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; @@ -54,14 +52,22 @@ import javax.imageio.ImageIO; import javax.swing.JPanel; import javax.swing.SwingUtilities; import org.controlsfx.control.MaskerPane; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.python.google.common.collect.Lists; import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog; import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog.TagNameAndComment; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.contentviewers.imagetagging.ControlType; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager; +import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager.ContentViewerTag; +import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager.SerializationException; +import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagControls; +import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion; +import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagCreator; +import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTag; +import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsGroup; import org.sleuthkit.autopsy.coreutils.ImageUtils; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.datamodel.AbstractFile; @@ -79,14 +85,15 @@ import org.sleuthkit.datamodel.TskCoreException; class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPanel { private static final Image EXTERNAL = new Image(MediaViewImagePanel.class.getResource("/org/sleuthkit/autopsy/images/external.png").toExternalForm()); + private final Logger LOGGER = Logger.getLogger(MediaViewImagePanel.class.getName()); private final boolean fxInited; private JFXPanel fxPanel; private AbstractFile file; - private StoredTag lastFocused; - private TopLevelTagsGroup imageGroup; - private ImageTagCreator tagger; + private Group masterGroup; + private ImageTagsGroup tagsGroup; + private ImageTagCreator imageTagCreator; private ImageView fxImageView; private ScrollPane scrollPane; private final ProgressBar progressBar = new ProgressBar(); @@ -132,22 +139,29 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan // build jfx ui (we could do this in FXML?) fxImageView = new ImageView(); // will hold image - imageGroup = new TopLevelTagsGroup(fxImageView); + masterGroup = new Group(fxImageView); + tagsGroup = new ImageTagsGroup(fxImageView); + masterGroup.getChildren().add(tagsGroup); deleteTagButton.setEnabled(false); - editTagButton.setEnabled(false); - imageGroup.addFocusChangeListener((event) -> { - if (event.getType() == ControlType.NOT_FOCUSED || event.getNode().equals(fxImageView)) { + + //Update buttons when users select (or unselect) image tags. + tagsGroup.addFocusChangeListener((event) -> { + if (event.getPropertyName().equals(ImageTagControls.NOT_FOCUSED.getName())) { deleteTagButton.setEnabled(false); - createTagButton.setEnabled(true); - editTagButton.setEnabled(false); - } else if (event.getType() == ControlType.FOCUSED) { + if (DisplayOptions.HIDE_TAGS.getName().equals(showTagsButton.getText())) { + createTagButton.setEnabled(true); + } + } else if (event.getPropertyName().equals(ImageTagControls.FOCUSED.getName())) { deleteTagButton.setEnabled(true); - editTagButton.setEnabled(true); createTagButton.setEnabled(false); - lastFocused = event.getNode(); + if (masterGroup.getChildren().contains(imageTagCreator)) { + imageTagCreator.disconnect(); + masterGroup.getChildren().remove(imageTagCreator); + } } }); - scrollPane = new ScrollPane(imageGroup); // scrolls and sizes imageview + + scrollPane = new ScrollPane(masterGroup); // scrolls and sizes imageview scrollPane.getStyleClass().add("bg"); //NOI18N scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED); scrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED); @@ -178,9 +192,13 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan Platform.runLater(() -> { fxImageView.setViewport(new Rectangle2D(0, 0, 0, 0)); fxImageView.setImage(null); - imageGroup.getChildren().clear(); + masterGroup.getChildren().clear(); + if (imageTagCreator != null) { + imageTagCreator.disconnect(); + } + showTagsButton.setText("Hide Tags"); scrollPane.setContent(null); - scrollPane.setContent(imageGroup); + scrollPane.setContent(masterGroup); }); } @@ -228,14 +246,28 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan try { Image fxImage = readImageTask.get(); - imageGroup.getChildren().clear(); - imageGroup.getChildren().add(fxImageView); + masterGroup.getChildren().clear(); + tagsGroup.getChildren().clear(); this.file = file; if (nonNull(fxImage)) { // We have a non-null image, so let's show it. fxImageView.setImage(fxImage); resetView(); - scrollPane.setContent(imageGroup); + masterGroup.getChildren().add(fxImageView); + masterGroup.getChildren().add(tagsGroup); + + try { + List tags = Case.getCurrentCase().getServices() + .getTagsManager().getContentTagsByContent(file); + + List> contentViewerTags = getContentViewerTags(tags); + //Add all image tags + tagsGroup = buildImageTagsGroup(contentViewerTags); + } catch (TskCoreException | NoCurrentCaseException ex) { + LOGGER.log(Level.WARNING, "Could not retrieve image tags for file in case db", ex); //NON-NLS + //TODO - pop dialog + } + scrollPane.setContent(masterGroup); } else { showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file); } @@ -275,6 +307,65 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan }); } + /** + * Finds all ContentViewerTags that are of type 'ImageTagRegion' for the + * current file. + * + * @param contentTags + * @return + * @throws TskCoreException + * @throws NoCurrentCaseException + */ + private List> getContentViewerTags(List contentTags) + throws TskCoreException, NoCurrentCaseException { + List> contentViewerTags = new ArrayList<>(); + for (ContentTag contentTag : contentTags) { + ContentViewerTag contentViewerTag = ContentViewerTagManager + .getTag(contentTag, ImageTagRegion.class); + if (contentViewerTag == null) { + continue; + } + + contentViewerTags.add(contentViewerTag); + } + return contentViewerTags; + } + + /** + * Builds ImageTag instances from stored ContentViewerTags of the + * appropriate type. + * + * @param contentTags + * @return + * @throws TskCoreException + * @throws NoCurrentCaseException + */ + private ImageTagsGroup buildImageTagsGroup(List> contentViewerTags) { + + contentViewerTags.forEach(contentViewerTag -> { + /** + * Build the image tag, add an edit event call back to persist all + * edits made on this image tag instance. + */ + ImageTag imageTag = new ImageTag(contentViewerTag, fxImageView); + imageTag.subscribeToEditEvents((event) -> { + try { + scrollPane.setCursor(Cursor.WAIT); + ImageTagRegion newRegion = (ImageTagRegion) event.getNewValue(); + ContentViewerTagManager.updateTag(contentViewerTag, newRegion); + scrollPane.setCursor(Cursor.DEFAULT); + } catch (SerializationException | NoCurrentCaseException | TskCoreException ex) { + LOGGER.log(Level.WARNING, "Could not save edit for image tag in case db", ex); //NON-NLS + //TODO - pop dialog + } + }); + + tagsGroup.getChildren().add(imageTag); + }); + + return tagsGroup; + } + /** * @return supported mime types */ @@ -329,13 +420,13 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan zoomInButton = new javax.swing.JButton(); jSeparator2 = new javax.swing.JToolBar.Separator(); zoomResetButton = new javax.swing.JButton(); - jSeparator3 = new javax.swing.JToolBar.Separator(); filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0)); + filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0)); createTagButton = new javax.swing.JButton(); jSeparator4 = new javax.swing.JToolBar.Separator(); - editTagButton = new javax.swing.JButton(); - jSeparator5 = new javax.swing.JToolBar.Separator(); deleteTagButton = new javax.swing.JButton(); + jSeparator3 = new javax.swing.JToolBar.Separator(); + showTagsButton = new javax.swing.JButton(); org.openide.awt.Mnemonics.setLocalizedText(jMenu1, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.jMenu1.text")); // NOI18N @@ -441,8 +532,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan } }); toolbar.add(zoomResetButton); - toolbar.add(jSeparator3); toolbar.add(filler1); + toolbar.add(filler2); org.openide.awt.Mnemonics.setLocalizedText(createTagButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.createTagButton.text")); // NOI18N createTagButton.setFocusable(false); @@ -457,18 +548,6 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan toolbar.add(createTagButton); toolbar.add(jSeparator4); - org.openide.awt.Mnemonics.setLocalizedText(editTagButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.editTagButton.text")); // NOI18N - editTagButton.setFocusable(false); - editTagButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - editTagButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - editTagButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - editTagButtonActionPerformed(evt); - } - }); - toolbar.add(editTagButton); - toolbar.add(jSeparator5); - org.openide.awt.Mnemonics.setLocalizedText(deleteTagButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.deleteTagButton.text")); // NOI18N deleteTagButton.setFocusable(false); deleteTagButton.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); @@ -480,6 +559,18 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan } }); toolbar.add(deleteTagButton); + toolbar.add(jSeparator3); + + org.openide.awt.Mnemonics.setLocalizedText(showTagsButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.showTagsButton.text")); // NOI18N + showTagsButton.setFocusable(false); + showTagsButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + showTagsButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + showTagsButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + showTagsButtonActionPerformed(evt); + } + }); + toolbar.add(showTagsButton); add(toolbar); }// //GEN-END:initComponents @@ -526,52 +617,162 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private void deleteTagButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteTagButtonActionPerformed Platform.runLater(() -> { - imageGroup.deleteNode(lastFocused); - }); - try { - Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(lastFocused.getContentTag()); - } catch (TskCoreException ex) { + ImageTag tagInFocus = tagsGroup.getFocus(); + //Null should not be expected, but just as a safetly precaution + if (tagInFocus == null) { + return; + } - } + try { + ContentViewerTag contentViewerTag = tagInFocus.getContentViewerTag(); + scrollPane.setCursor(Cursor.WAIT); + ContentViewerTagManager.deleteTag(contentViewerTag); + Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(contentViewerTag.getContentTag()); + tagsGroup.getChildren().remove(tagInFocus); + } catch (TskCoreException | NoCurrentCaseException ex) { + LOGGER.log(Level.WARNING, "Could not delete image tag in case db", ex); //NON-NLS + //TODO pop dialog + } + + scrollPane.setCursor(Cursor.DEFAULT); + }); + + deleteTagButton.setEnabled(false); + createTagButton.setEnabled(true); }//GEN-LAST:event_deleteTagButtonActionPerformed private void createTagButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createTagButtonActionPerformed - Platform.runLater(() -> { - tagger = new ImageTagCreator(fxImageView); - StoredTagListener newTagListener = (StoredTagEvent event) -> { - StoredTag tag = event.getTag(); - imageGroup.getChildren().add(tag); - SwingUtilities.invokeLater(() -> { - TagNameAndComment result = GetTagNameAndCommentDialog.doDialog(); - if (result != null) { - try { - ContentTag t = Case.getCurrentCase().getServices().getTagsManager().addContentTag(file, - result.getTagName(), result.getComment()); - tag.addContentTag(t); - } catch (TskCoreException ex) { - Platform.runLater(() -> imageGroup.deleteNode(tag)); - } - } else { - Platform.runLater(() -> imageGroup.deleteNode(tag)); + createTagButton.setEnabled(false); + showTagsButton.setEnabled(false); + imageTagCreator = new ImageTagCreator(fxImageView); + + PropertyChangeListener newTagListener = (event) -> { + + SwingUtilities.invokeLater(() -> { + ImageTagRegion tag = (ImageTagRegion) event.getNewValue(); + //Ask the user for tag name and comment + TagNameAndComment result = GetTagNameAndCommentDialog.doDialog(); + if (result == null) { + createTagButton.setEnabled(true); + showTagsButton.setEnabled(true); + return; + } + + //Persist and build image tag + Platform.runLater(() -> { + try { + ContentViewerTag contentViewerTag = storeImageTag(tag, result); + ImageTag imageTag = buildImageTag(contentViewerTag); + tagsGroup.getChildren().add(imageTag); + } catch (TskCoreException | SerializationException | NoCurrentCaseException ex) { + LOGGER.log(Level.WARNING, "Could not save new image tag in case db", ex); //NON-NLS + //TODO pop dialog } }); - imageGroup.deleteNode(tagger); + }); - }; - tagger.addNewTagListener(newTagListener); - imageGroup.getChildren().add(tagger); - }); + //Remove image tag creator from panel + Platform.runLater(() -> { + imageTagCreator.disconnect(); + masterGroup.getChildren().remove(imageTagCreator); + }); + }; + + imageTagCreator.addNewTagListener(newTagListener); + Platform.runLater(() -> masterGroup.getChildren().add(imageTagCreator)); }//GEN-LAST:event_createTagButtonActionPerformed - private void editTagButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editTagButtonActionPerformed - ContentTag t = lastFocused.getContentTag(); - }//GEN-LAST:event_editTagButtonActionPerformed + /** + * + * @param contentViewerTag + * @return + */ + private ImageTag buildImageTag(ContentViewerTag contentViewerTag) { + ImageTag imageTag = new ImageTag(contentViewerTag, fxImageView); + + //Automatically persist edits made by user + imageTag.subscribeToEditEvents((edit) -> { + try { + scrollPane.setCursor(Cursor.WAIT); + ImageTagRegion newRegion = (ImageTagRegion) edit.getNewValue(); + ContentViewerTagManager.updateTag(contentViewerTag, newRegion); + scrollPane.setCursor(Cursor.DEFAULT); + } catch (SerializationException | TskCoreException | NoCurrentCaseException ex) { + LOGGER.log(Level.WARNING, "Could not save edit for image tag in case db", ex); //NON-NLS + //TODO pop dialog + } + }); + return imageTag; + } + + /** + * Stores the image tag by creating a ContentTag instance and associating + * the ImageTagRegion data with it in the case database. + * + * @param data + * @param result + */ + private ContentViewerTag storeImageTag(ImageTagRegion data, TagNameAndComment result) + throws TskCoreException, SerializationException, NoCurrentCaseException { + try { + scrollPane.setCursor(Cursor.WAIT); + ContentTag contentTag = Case.getCurrentCaseThrows().getServices().getTagsManager() + .addContentTag(file, result.getTagName(), result.getComment()); + ContentViewerTag contentViewerTag = ContentViewerTagManager.saveTag(contentTag, data); + return contentViewerTag; + } finally { + scrollPane.setCursor(Cursor.DEFAULT); + createTagButton.setEnabled(true); + showTagsButton.setEnabled(true); + } + } + + private void showTagsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showTagsButtonActionPerformed + Platform.runLater(() -> { + if (DisplayOptions.HIDE_TAGS.getName().equals(showTagsButton.getText())) { + //Temporarily remove the tags group and update buttons + masterGroup.getChildren().remove(tagsGroup); + showTagsButton.setText(DisplayOptions.SHOW_TAGS.getName()); + createTagButton.setEnabled(false); + deleteTagButton.setEnabled(false); + } else { + //Add tags group back in and update buttons + masterGroup.getChildren().add(tagsGroup); + showTagsButton.setText(DisplayOptions.HIDE_TAGS.getName()); + if (tagsGroup.getFocus() != null) { + createTagButton.setEnabled(false); + deleteTagButton.setEnabled(true); + } else { + createTagButton.setEnabled(true); + deleteTagButton.setEnabled(false); + } + } + }); + }//GEN-LAST:event_showTagsButtonActionPerformed + + /** + * Display states for the show/hide tags button. + */ + enum DisplayOptions { + HIDE_TAGS("Hide Tags"), + SHOW_TAGS("Show Tags"); + + private final String name; + + DisplayOptions(String name) { + this.name = name; + } + + String getName() { + return name; + } + } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton createTagButton; private javax.swing.JButton deleteTagButton; - private javax.swing.JButton editTagButton; private javax.swing.Box.Filler filler1; + private javax.swing.Box.Filler filler2; private javax.swing.JMenu jMenu1; private javax.swing.JPopupMenu jPopupMenu1; private javax.swing.JPopupMenu jPopupMenu2; @@ -579,10 +780,10 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private javax.swing.JToolBar.Separator jSeparator2; private javax.swing.JToolBar.Separator jSeparator3; private javax.swing.JToolBar.Separator jSeparator4; - private javax.swing.JToolBar.Separator jSeparator5; private javax.swing.JButton rotateLeftButton; private javax.swing.JButton rotateRightButton; private javax.swing.JTextField rotationTextField; + private javax.swing.JButton showTagsButton; private javax.swing.JToolBar toolbar; private javax.swing.JButton zoomInButton; private javax.swing.JButton zoomOutButton; @@ -728,8 +929,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan // Add the transforms in reverse order of intended execution. // Note: They MUST be added in this order to ensure translate is // executed last. - imageGroup.getTransforms().clear(); - imageGroup.getTransforms().addAll(translate, rotate, scale); + masterGroup.getTransforms().clear(); + masterGroup.getTransforms().addAll(translate, rotate, scale); // Adjust scroll bar positions for view changes. if (viewportWidth > fxPanel.getWidth()) { diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeEvent.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeEvent.java deleted file mode 100755 index f8cac1a128..0000000000 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeEvent.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.contentviewers.imagetagging; - -import java.util.EventObject; -import javafx.event.Event; -import javafx.event.EventType; - -/** - * - * @author dsmyda - */ -public final class FocusChangeEvent extends EventObject{ - - private final EventType type; - private final StoredTag focused; - - public FocusChangeEvent(Object source, EventType type, StoredTag focused) { - super(source); - this.type = type; - this.focused = focused; - } - - public EventType getType() { - return type; - } - - public StoredTag getNode() { - return focused; - } - -} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeListener.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeListener.java deleted file mode 100755 index 6699e57002..0000000000 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/FocusChangeListener.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.sleuthkit.autopsy.contentviewers.imagetagging; - -import java.util.EventListener; - -/** - * - * @author dsmyda - */ -@FunctionalInterface -public interface FocusChangeListener extends EventListener{ - - void focusChanged(FocusChangeEvent event); -} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java new file mode 100755 index 0000000000..e54d365331 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java @@ -0,0 +1,343 @@ +/* + * 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.contentviewers.imagetagging; + +import com.sun.javafx.event.EventDispatchChainImpl; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import javafx.collections.ListChangeListener; +import javafx.scene.Cursor; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.control.Tooltip; +import javafx.scene.image.ImageView; +import javafx.scene.input.MouseEvent; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; +import javafx.scene.shape.Rectangle; +import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager.ContentViewerTag; + +/** + * A tagged region displayed over an image. This class contains a "physical tag" + * and 8 edit "handles". The physical tag is a plain old rectangle that defines + * the tag boundaries. The edit handles serve two purposes. One is to represent + * selection. All 8 edit handles will become visible overtop the physical tag + * when the user clicks on the rectangle. The other purpose is to allow the user to edit + * and manipulate the physical tag boundaries (hence the name, edit handle). + * This class should be treated as a logical image tag. + */ +public final class ImageTag extends Group { + + // Used to tell the 8 edit handles to hide if this tag is no longer selected + private final EventDispatchChainImpl ALL_CHILDREN; + + //Notifies listeners that the user has editted the tag boundaries + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + + //The underlying presistent tag details that this image tag originates from + private final ContentViewerTag appTag; + + public ImageTag(ContentViewerTag appTag, ImageView image) { + ALL_CHILDREN = new EventDispatchChainImpl(); + this.appTag = appTag; + + this.getChildren().addListener((ListChangeListener) change -> { + change.next(); + change.getAddedSubList().forEach((node) -> ALL_CHILDREN.append(node.getEventDispatcher())); + }); + + ImageTagRegion details = appTag.getDetails(); + PhysicalTag physicalTag = new PhysicalTag(details); + + //Defines the max allowable boundary that a user may drag any given handle. + Boundary dragBoundary = (x, y) -> { + double boundingX = image.getX(); + double boundingY = image.getY(); + double width = image.getImage().getWidth(); + double height = image.getImage().getHeight(); + + return x > boundingX + details.getStrokeThickness() / 2 + && x < boundingX + width - details.getStrokeThickness() / 2 + && y > boundingY + details.getStrokeThickness() / 2 + && y < boundingY + height - details.getStrokeThickness() / 2; + }; + + EditHandle bottomLeft = new EditHandle(physicalTag) + .setPosition(Position.bottom(), Position.left()) + .setDrag(dragBoundary, Drag.bottom(), Drag.left()); + + EditHandle bottomRight = new EditHandle(physicalTag) + .setPosition(Position.bottom(), Position.right()) + .setDrag(dragBoundary, Drag.bottom(), Drag.right()); + + EditHandle topLeft = new EditHandle(physicalTag) + .setPosition(Position.top(), Position.left()) + .setDrag(dragBoundary, Drag.top(), Drag.left()); + + EditHandle topRight = new EditHandle(physicalTag) + .setPosition(Position.top(), Position.right()) + .setDrag(dragBoundary, Drag.top(), Drag.right()); + + EditHandle bottomMiddle = new EditHandle(physicalTag) + .setPosition(Position.bottom(), Position.xMiddle()) + .setDrag(dragBoundary, Drag.bottom()); + + EditHandle topMiddle = new EditHandle(physicalTag) + .setPosition(Position.top(), Position.xMiddle()) + .setDrag(dragBoundary, Drag.top()); + + EditHandle rightMiddle = new EditHandle(physicalTag) + .setPosition(Position.right(), Position.yMiddle()) + .setDrag(dragBoundary, Drag.right()); + + EditHandle leftMiddle = new EditHandle(physicalTag) + .setPosition(Position.left(), Position.yMiddle()) + .setDrag(dragBoundary, Drag.left()); + + //The "logical" tag is the Group + this.getChildren().addAll(physicalTag, bottomLeft, bottomRight, topLeft, + topRight, bottomMiddle, topMiddle, rightMiddle, leftMiddle); + + Tooltip.install(this, new Tooltip(appTag.getContentTag() + .getName().getDisplayName())); + + this.addEventHandler(ImageTagControls.NOT_FOCUSED, event -> ALL_CHILDREN.dispatchEvent(event)); + this.addEventHandler(ImageTagControls.FOCUSED, event -> ALL_CHILDREN.dispatchEvent(event)); + } + + /** + * Add a new listener for edit events. These events are generated when a + * user drags on one of the edit "knobs" of the tag. + * + * @param listener + */ + public void subscribeToEditEvents(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + /** + * Get the app tag that this class represents. + * + * @return + */ + public ContentViewerTag getContentViewerTag() { + return appTag; + } + + /** + * Plain old rectangle that represents an unselected Image Tag + */ + class PhysicalTag extends Rectangle { + + public PhysicalTag(ImageTagRegion details) { + this.setStroke(Color.RED); + this.setFill(Color.RED.deriveColor(0, 0, 0, 0)); + this.setStrokeWidth(details.getStrokeThickness()); + + setX(details.getX()); + setY(details.getY()); + setWidth(details.getWidth()); + setHeight(details.getHeight()); + + this.addEventHandler(ImageTagControls.NOT_FOCUSED, event -> this.setOpacity(1)); + this.addEventHandler(ImageTagControls.FOCUSED, event -> this.setOpacity(0.5)); + } + + /** + * Builds a portable description of the tag region. + * + * @return + */ + public ImageTagRegion getState() { + return new ImageTagRegion() + .setX(this.getX()) + .setY(this.getY()) + .setWidth(this.getWidth()) + .setHeight(this.getHeight()) + .setStrokeThickness(this.getStrokeWidth()); + } + } + + /** + * Draggable "knob" used to manipulate the physical tag boundaries. + */ + class EditHandle extends Circle { + + private final PhysicalTag parent; + + public EditHandle(PhysicalTag parent) { + this.setVisible(false); + + //Hide when the tag is not selected. + this.addEventHandler(ImageTagControls.NOT_FOCUSED, event -> this.setVisible(false)); + this.addEventHandler(ImageTagControls.FOCUSED, event -> this.setVisible(true)); + + this.setRadius(parent.getStrokeWidth()); + this.setFill(parent.getStroke()); + + this.setOnDragDetected(event -> { + this.getParent().setCursor(Cursor.CLOSED_HAND); + }); + + this.setOnMouseReleased(event -> { + this.getParent().setCursor(Cursor.DEFAULT); + pcs.firePropertyChange(new PropertyChangeEvent(this, "Tag Edit", null, parent.getState())); + }); + + this.parent = parent; + } + + /** + * Sets the positioning of this edit handle on the physical tag. + * + * @param vals + * @return + */ + public EditHandle setPosition(Position... vals) { + for (Position pos : vals) { + parent.widthProperty().addListener((obs, oldVal, newVal) -> pos.set(parent, this)); + parent.heightProperty().addListener((obs, oldVal, newVal) -> pos.set(parent, this)); + pos.set(parent, this); + } + return this; + } + + /** + * Sets the drag capabilities for manipulating the physical tag. + * + * @param bounds + * @param vals + * @return + */ + public EditHandle setDrag(Boundary bounds, Drag... vals) { + this.setOnMouseDragged((event) -> { + for (Drag drag : vals) { + drag.perform(parent, event, bounds); + } + }); + return this; + } + } + + /** + * Position strategies for "sticking" to a location on the physical tag when + * it is resized. + */ + static interface Position { + + void set(PhysicalTag parent, Circle knob); + + static Position left() { + return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty()); + } + + static Position right() { + return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty().add(parent.getWidth())); + } + + static Position top() { + return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty()); + } + + static Position bottom() { + return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty().add(parent.getHeight())); + } + + static Position xMiddle() { + return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty().add(parent.getWidth() / 2)); + } + + static Position yMiddle() { + return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty().add(parent.getHeight() / 2)); + } + } + + /** + * Defines the bounding box for which dragging is allowable. + */ + @FunctionalInterface + static interface Boundary { + + boolean isPointInBounds(double x, double y); + } + + /** + * Drag strategies for manipulating the physical tag from a given side of + * the rectangle. + */ + static interface Drag { + + void perform(PhysicalTag parent, MouseEvent event, Boundary b); + + static Drag bottom() { + return (parent, event, bounds) -> { + if (!bounds.isPointInBounds(event.getX(), event.getY())) { + return; + } + + double deltaY = event.getY() - parent.getY(); + if (deltaY > 0) { + parent.setHeight(deltaY); + } + }; + } + + static Drag top() { + return (parent, event, bounds) -> { + if (!bounds.isPointInBounds(event.getX(), event.getY())) { + return; + } + + double deltaY = parent.getY() + parent.getHeight() - event.getY(); + if (deltaY < parent.getY() + parent.getHeight() && deltaY > 0) { + parent.setHeight(deltaY); + parent.setY(event.getY()); + } + }; + } + + static Drag left() { + return (parent, event, bounds) -> { + if (!bounds.isPointInBounds(event.getX(), event.getY())) { + return; + } + + double deltaX = parent.getX() + parent.getWidth() - event.getX(); + if (deltaX < parent.getX() + parent.getWidth() && deltaX > 0) { + parent.setWidth(deltaX); + parent.setX(event.getX()); + } + }; + } + + static Drag right() { + return (parent, event, bounds) -> { + if (!bounds.isPointInBounds(event.getX(), event.getY())) { + return; + } + + double deltaX = event.getX() - parent.getX(); + if (deltaX > 0) { + parent.setWidth(deltaX); + } + }; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ControlType.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagControls.java similarity index 84% rename from Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ControlType.java rename to Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagControls.java index 6e0c6a2493..d4ee799262 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ControlType.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagControls.java @@ -22,11 +22,10 @@ import javafx.event.Event; import javafx.event.EventType; /** - * - * @author dsmyda + * Focus events for ImageTags to consume. These events trigger selection behavior + * on ImageTags and are originated from the ImageTagsGroup class. */ -public class ControlType { +public class ImageTagControls { public static final EventType NOT_FOCUSED = new EventType<>("NOT_FOCUSED"); public static final EventType FOCUSED = new EventType<>("FOCUSED"); - public static final EventType DELETE = new EventType<>("DELETE"); } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java index b0c114ada5..4df4328b2d 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java @@ -16,11 +16,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.sleuthkit.autopsy.contentviewers.imagetagging; -import java.util.ArrayList; -import java.util.Collection; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; import javafx.event.EventHandler; import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; @@ -28,7 +28,10 @@ import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; /** - * Creates image tags. This tool can be treated like any other JavaFX node. + * Creates image tags. This class attaches itself to a source image, waiting + * for mouse press, mouse drag, and mouse release events. Upon a mouse release + * event, any listeners are updated with the portable description of the new tag + * boundaries (ImageTagRegion). */ public final class ImageTagCreator extends Rectangle { @@ -39,12 +42,18 @@ public final class ImageTagCreator extends Rectangle { //a good balance between visual acuity and loss of selection at the borders //of the image. private double lineThicknessAsPercent = 1.5; - private final double minArea; - private final Collection listeners; + private final double minArea; + //Used to update listeners of the new tag boundaries + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private final EventHandler mousePressed; private final EventHandler mouseDragged; private final EventHandler mouseReleased; + + //Handles the unregistering this ImageTagCreator from mouse press, mouse drag, + //and mouse release events of the source image. + private final Runnable disconnect; /** * Adds tagging support to an image, where the 'tag' rectangle will be the @@ -53,8 +62,6 @@ public final class ImageTagCreator extends Rectangle { * @param image Image to tag */ public ImageTagCreator(ImageView image) { - listeners = new ArrayList<>(); - setStroke(Color.RED); setFill(Color.RED.deriveColor(0, 0, 0, 0)); @@ -65,7 +72,7 @@ public final class ImageTagCreator extends Rectangle { setStrokeWidth(lineThicknessPixels); minArea = lineThicknessPixels * lineThicknessPixels; setVisible(false); - + this.mousePressed = (MouseEvent event) -> { if (event.isSecondaryButtonDown()) { return; @@ -79,9 +86,9 @@ public final class ImageTagCreator extends Rectangle { setX(rectangleOriginX); setY(rectangleOriginY); }; - + image.addEventHandler(MouseEvent.MOUSE_PRESSED, this.mousePressed); - + this.mouseDragged = (MouseEvent event) -> { if (event.isSecondaryButtonDown()) { return; @@ -111,45 +118,55 @@ public final class ImageTagCreator extends Rectangle { } setHeight(Math.abs(offsetY)); }; - + image.addEventHandler(MouseEvent.MOUSE_DRAGGED, this.mouseDragged); - + this.mouseReleased = event -> { - if ((this.getWidth() - this.getStrokeWidth()) * - (this.getHeight() - this.getStrokeWidth()) <= minArea) { + //Reject any drags that are too small to count as a meaningful tag. + //Meaningful is described as having an area that is visible that is + //not consumed by the thickness of the stroke. + if ((this.getWidth() - this.getStrokeWidth()) + * (this.getHeight() - this.getStrokeWidth()) <= minArea) { defaultSettings(); return; - } - - //Notify listeners - StoredTagEvent newTagEvent = new StoredTagEvent(this, new StoredTag(image, this.getX(), this.getY(), - this.getX() + this.getWidth(), this.getY() + this.getHeight())); - - listeners.forEach((listener) -> { - listener.newTagEvent(newTagEvent); - }); - - defaultSettings(); + } + + this.pcs.firePropertyChange(new PropertyChangeEvent(this, "New Tag", + null, new ImageTagRegion() + .setX(this.getX()) + .setY(this.getY()) + .setWidth(this.getWidth()) + .setHeight(this.getHeight()) + .setStrokeThickness(lineThicknessPixels))); }; - + image.addEventHandler(MouseEvent.MOUSE_RELEASED, this.mouseReleased); - this.addEventHandler(ControlType.NOT_FOCUSED, (event) -> { + + //Used to remove itself from mouse events on the source image + disconnect = () -> { defaultSettings(); image.removeEventHandler(MouseEvent.MOUSE_RELEASED, mouseReleased); image.removeEventHandler(MouseEvent.MOUSE_DRAGGED, mouseDragged); image.removeEventHandler(MouseEvent.MOUSE_PRESSED, mousePressed); - }); - - this.addEventHandler(ControlType.DELETE, event -> { - defaultSettings(); - image.removeEventHandler(MouseEvent.MOUSE_RELEASED, mouseReleased); - image.removeEventHandler(MouseEvent.MOUSE_DRAGGED, mouseDragged); - image.removeEventHandler(MouseEvent.MOUSE_PRESSED, mousePressed); - }); + }; } - - public void addNewTagListener(StoredTagListener listener) { - listeners.add(listener); + + /** + * Registers a PCL for new tag events. Listeners are updated with a portable + * description (ImageTagRegion) of the new tag, which represent the + * rectangle boundaries. + * + * @param listener + */ + public void addNewTagListener(PropertyChangeListener listener) { + this.pcs.addPropertyChangeListener(listener); + } + + /** + * Removes itself from mouse events on the source image. + */ + public void disconnect() { + this.disconnect.run(); } /** diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagRegion.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagRegion.java new file mode 100755 index 0000000000..b4ba2035b0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagRegion.java @@ -0,0 +1,82 @@ +/* + * 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.contentviewers.imagetagging; + +/** + * Bean representation of an image tag. This class is used for storage and + * retrieval of ImageTags from the case database. + */ +public class ImageTagRegion { + + /** + * These fields will be serialized and stored in the case database by the + * ContentViewerTagManager. + */ + private double x; + private double y; + private double width; + private double height; + + private double strokeThickness; + + public ImageTagRegion setStrokeThickness(double thickness) { + this.strokeThickness = thickness; + return this; + } + + public ImageTagRegion setX(double x) { + this.x = x; + return this; + } + + public ImageTagRegion setWidth(double width) { + this.width = width; + return this; + } + + public ImageTagRegion setY(double y) { + this.y = y; + return this; + } + + public ImageTagRegion setHeight(double height) { + this.height = height; + return this; + } + + public double getX() { + return x; + } + + public double getWidth() { + return width; + } + + public double getY() { + return y; + } + + public double getHeight() { + return height; + } + + public double getStrokeThickness() { + return strokeThickness; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java new file mode 100755 index 0000000000..86257e55f8 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java @@ -0,0 +1,124 @@ +/* + * 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.contentviewers.imagetagging; + +import com.sun.javafx.event.EventDispatchChainImpl; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import javafx.event.Event; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.input.MouseEvent; + +/** + * Manages the focus and z-ordering of ImageTags. Only one image tag may be + * selected at a time. Image tags show their 8 edit "handles" upon selection + * (see ImageTag class for more details) and get the highest z-ordering to make + * editing easier. This class is responsible for setting and dropping focus as + * the user navigates from tag to tag. The ImageTag is treated as a logical + * unit, however it's underlying representation consists of the physical + * rectangle and the 8 edit handles. JavaFX will report selection on the Node + * level (so either the Rectangle, or a singe edit handle), which makes keeping + * the entire image tag in focus a non-trivial problem. + */ +public final class ImageTagsGroup extends Group { + + private final EventDispatchChainImpl NO_OP_CHAIN = new EventDispatchChainImpl(); + private final PropertyChangeSupport pcl = new PropertyChangeSupport(this); + + private volatile ImageTag currentFocus; + + public ImageTagsGroup(Node backDrop) { + + //Reset focus of current selection if the back drop has focus. + backDrop.setOnMousePressed((mouseEvent) -> { + if (currentFocus != null) { + currentFocus.getEventDispatcher().dispatchEvent( + new Event(ImageTagControls.NOT_FOCUSED), NO_OP_CHAIN); + currentFocus = null; + } + + this.pcl.firePropertyChange(new PropertyChangeEvent(this, + ImageTagControls.NOT_FOCUSED.getName(), currentFocus, null)); + }); + + //Set the focus of selected tag + this.addEventFilter(MouseEvent.MOUSE_PRESSED, (MouseEvent e) -> { + if (!e.isPrimaryButtonDown()) { + return; + } + + //Pull out the logical image tag that this node is associated with + Node topLevelChild = e.getPickResult().getIntersectedNode(); + while (!this.getChildren().contains(topLevelChild)) { + topLevelChild = topLevelChild.getParent(); + } + + requestFocus((ImageTag) topLevelChild); + }); + } + + /** + * Subscribe to focus change events on Image tags. + * + * @param fcl PCL to be notified which Image tag has been selected. + */ + public void addFocusChangeListener(PropertyChangeListener fcl) { + this.pcl.addPropertyChangeListener(fcl); + } + + /** + * Get the image tag that current has focus. + * + * @return ImageTag instance or null if no tag is in focus. + */ + public ImageTag getFocus() { + return currentFocus; + } + + /** + * Notifies the logical image tag that it is no longer in focus. + * + * @param n + */ + private void resetFocus(ImageTag n) { + n.getEventDispatcher().dispatchEvent(new Event(ImageTagControls.NOT_FOCUSED), NO_OP_CHAIN); + this.pcl.firePropertyChange(new PropertyChangeEvent(this, ImageTagControls.NOT_FOCUSED.getName(), n, null)); + } + + /** + * Notifies the logical image that it is in focus. + * + * @param n + */ + private void requestFocus(ImageTag n) { + if (currentFocus == n) { + return; + } else if (currentFocus != null && currentFocus != n) { + resetFocus(currentFocus); + } + + n.getEventDispatcher().dispatchEvent(new Event(ImageTagControls.FOCUSED), NO_OP_CHAIN); + this.pcl.firePropertyChange(new PropertyChangeEvent(this, ImageTagControls.FOCUSED.getName(), currentFocus, n)); + + currentFocus = n; + n.toFront(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTag.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTag.java deleted file mode 100755 index 1a97918dee..0000000000 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTag.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * 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.contentviewers.imagetagging; - -import com.sun.javafx.event.EventDispatchChainImpl; -import javafx.collections.ListChangeListener; -import javafx.scene.Cursor; -import javafx.scene.Group; -import javafx.scene.Node; -import javafx.scene.image.ImageView; -import javafx.scene.input.MouseEvent; -import javafx.scene.paint.Color; -import javafx.scene.shape.Circle; -import javafx.scene.shape.Rectangle; -import org.sleuthkit.datamodel.ContentTag; - -/** - * - * @author dsmyda - */ -public final class StoredTag extends Group { - - private final EventDispatchChainImpl ALL_CHILDREN; - private final PhysicalTag physicalTag; - - public StoredTag(ImageView image, double x, double y, double x1, double y1) { - ALL_CHILDREN = new EventDispatchChainImpl(); - - this.getChildren().addListener((ListChangeListener) change -> { - change.next(); - change.getAddedSubList().forEach((node) -> ALL_CHILDREN.append(node.getEventDispatcher())); - }); - - double min = Math.min(image.getImage().getWidth(), image.getImage().getHeight()); - double lineThicknessPixels = min * 1.5 / 100.0; - physicalTag = new PhysicalTag(image); - physicalTag.setStrokeWidth(lineThicknessPixels); - - EditHandle bottomLeft = new EditHandle(); - bottomLeft.setPosition(Position.bottom(), Position.left()); - bottomLeft.setDrag(Drag.bottom(), Drag.left()); - - EditHandle bottomRight = new EditHandle(); - bottomRight.setPosition(Position.bottom(), Position.right()); - bottomRight.setDrag(Drag.bottom(), Drag.right()); - - EditHandle topLeft = new EditHandle(); - topLeft.setPosition(Position.top(), Position.left()); - topLeft.setDrag(Drag.top(), Drag.left()); - - EditHandle topRight = new EditHandle(); - topRight.setPosition(Position.top(), Position.right()); - topRight.setDrag(Drag.top(), Drag.right()); - - EditHandle bottomMiddle = new EditHandle(); - bottomMiddle.setPosition(Position.bottom(), Position.xMiddle()); - bottomMiddle.setDrag(Drag.bottom()); - - EditHandle topMiddle = new EditHandle(); - topMiddle.setPosition(Position.top(), Position.xMiddle()); - topMiddle.setDrag(Drag.top()); - - EditHandle rightMiddle = new EditHandle(); - rightMiddle.setPosition(Position.right(), Position.yMiddle()); - rightMiddle.setDrag(Drag.right()); - - EditHandle leftMiddle = new EditHandle(); - leftMiddle.setPosition(Position.left(), Position.yMiddle()); - leftMiddle.setDrag(Drag.left()); - - this.getChildren().addAll(physicalTag, bottomLeft, bottomRight, topLeft, - topRight, bottomMiddle, topMiddle, rightMiddle, leftMiddle); - - //Position the tag on the image. The edit knobs will be notified of - //the new coords and adjust themselves. - physicalTag.setX(x); - physicalTag.setY(y); - physicalTag.setWidth(x1 - x); - physicalTag.setHeight(y1 - y); - - this.focusedProperty().addListener((ov, oldV, newV) -> { - if(!newV) { - System.out.println("NOT FOCUSED"); - } else { - System.out.println("GAINED FOCUS"); - } - }); - - this.addEventHandler(ControlType.NOT_FOCUSED, event -> ALL_CHILDREN.dispatchEvent(event)); - this.addEventHandler(ControlType.FOCUSED, event -> ALL_CHILDREN.dispatchEvent(event)); - this.addEventHandler(ControlType.DELETE, event -> ALL_CHILDREN.dispatchEvent(event)); - } - - public void addContentTag(ContentTag t) { - physicalTag.linkContentTag(t); - } - - public ContentTag getContentTag() { - return physicalTag.getContentTag(); - } - - class PhysicalTag extends Rectangle { - - private final ImageView image; - private ContentTag tag; - - public PhysicalTag(ImageView image) { - this.setStroke(Color.RED); - this.setFill(Color.RED.deriveColor(0, 0, 0, 0)); - - this.addEventHandler(ControlType.NOT_FOCUSED, event -> this.setOpacity(1)); - this.addEventHandler(ControlType.FOCUSED, event -> this.setOpacity(0.5)); - - this.addEventHandler(ControlType.DELETE, event -> { - this.setVisible(false); - //TODO - delete tag from persistent storage here. - }); - - this.image = image; - } - - private void save() { - //TODO - persist tag - } - - public void linkContentTag(ContentTag t) { - tag = t; - } - - public ContentTag getContentTag() { - return tag; - } - - private ImageView getUnderlyingImage() { - return image; - } - - private class ImageRegion { - - } - } - - class EditHandle extends Circle { - - public EditHandle() { - super(physicalTag.getStrokeWidth(), physicalTag.getStroke()); - this.setVisible(false); - - //Manipulate the parent rectangle when this knob is dragged. - this.addEventHandler(ControlType.NOT_FOCUSED, event -> this.setVisible(false)); - this.addEventHandler(ControlType.FOCUSED, event -> this.setVisible(true)); - this.addEventHandler(ControlType.DELETE, event -> this.setVisible(false)); - this.setOnDragDetected(event -> this.getParent().setCursor(Cursor.CLOSED_HAND)); - this.setOnMouseReleased(event -> { - this.getParent().setCursor(Cursor.DEFAULT); - physicalTag.save(); - }); - } - - public void setPosition(Position... vals) { - for (Position pos : vals) { - physicalTag.widthProperty().addListener((obs, oldVal, newVal) -> pos.set(physicalTag, this)); - physicalTag.heightProperty().addListener((obs, oldVal, newVal) -> pos.set(physicalTag, this)); - } - } - - public void setDrag(Drag... vals) { - this.setOnMouseDragged((event) -> { - for (Drag drag : vals) { - drag.perform(physicalTag, event, physicalTag.getUnderlyingImage()); - } - }); - } - } - - static interface Position { - - void set(Rectangle parent, Circle knob); - - static Position left() { - return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty()); - } - - static Position right() { - return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty().add(parent.getWidth())); - } - - static Position top() { - return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty()); - } - - static Position bottom() { - return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty().add(parent.getHeight())); - } - - static Position xMiddle() { - return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty().add(parent.getWidth() / 2)); - } - - static Position yMiddle() { - return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty().add(parent.getHeight() / 2)); - } - } - - static interface Drag { - - void perform(Rectangle parent, MouseEvent event, ImageView image); - - static Drag bottom() { - return (parent, event, image) -> { - double deltaY = event.getY() - parent.getY(); - if (deltaY > 0 && event.getY() - < image.getY() + image.getImage().getHeight() - - parent.getStrokeWidth() / 2) { - parent.setHeight(deltaY); - } - }; - } - - static Drag top() { - return (parent, event, image) -> { - double deltaY = parent.getY() + parent.getHeight() - event.getY(); - if (deltaY < parent.getY() + parent.getHeight() && event.getY() - > image.getY() + parent.getStrokeWidth() / 2 && deltaY > 0) { - parent.setHeight(deltaY); - parent.setY(event.getY()); - } - }; - } - - static Drag left() { - return (parent, event, image) -> { - double deltaX = parent.getX() + parent.getWidth() - event.getX(); - if (deltaX < parent.getX() + parent.getWidth() - && event.getX() > image.getX() + parent.getStrokeWidth() / 2 - && deltaX > 0) { - parent.setWidth(deltaX); - parent.setX(event.getX()); - } - }; - } - - static Drag right() { - return (parent, event, image) -> { - double deltaX = event.getX() - parent.getX(); - if (deltaX > 0 && event.getX() < image.getX() - + image.getImage().getWidth() - parent.getStrokeWidth() / 2) { - parent.setWidth(deltaX); - } - }; - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagEvent.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagEvent.java deleted file mode 100755 index e1cba2d7f6..0000000000 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagEvent.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.contentviewers.imagetagging; - - -import java.util.EventObject; - -/** - * - * @author dsmyda - */ -public final class StoredTagEvent extends EventObject { - private final StoredTag tag; - - public StoredTagEvent(Object source, StoredTag tag) { - super(source); - this.tag = tag; - } - - public StoredTag getTag() { - return tag; - } -} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagListener.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagListener.java deleted file mode 100755 index a727cc4f53..0000000000 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/StoredTagListener.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.contentviewers.imagetagging; - - -import java.util.EventListener; - -/** - * - * @author dsmyda - */ -@FunctionalInterface -public interface StoredTagListener extends EventListener { - - void newTagEvent(StoredTagEvent tagEvent); -} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/TopLevelTagsGroup.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/TopLevelTagsGroup.java deleted file mode 100755 index 2a00df6f7a..0000000000 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/TopLevelTagsGroup.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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.contentviewers.imagetagging; - -import com.sun.javafx.event.EventDispatchChainImpl; -import java.util.ArrayList; -import java.util.Collection; -import javafx.event.Event; -import javafx.scene.Group; -import javafx.scene.Node; -import javafx.scene.image.ImageView; -import javafx.scene.input.MouseEvent; - -/** - * Top level group containing Image and all existing tags. - */ -public final class TopLevelTagsGroup extends Group { - - private final EventDispatchChainImpl NO_OP_CHAIN = new EventDispatchChainImpl(); - private final Collection listeners; - - private Node lastFocus; - private final ImageView baseImage; - - public TopLevelTagsGroup(ImageView image) { - super(image); - baseImage = image; - listeners = new ArrayList<>(); - - //Manage focus, such that only one child can be set at a time. - this.addEventFilter(MouseEvent.MOUSE_PRESSED, (MouseEvent e) -> { - if (!e.isPrimaryButtonDown()) { - return; - } - - Node topLevelChild = e.getPickResult().getIntersectedNode(); - while (!this.getChildren().contains(topLevelChild)) { - topLevelChild = topLevelChild.getParent(); - } - - requestFocus(topLevelChild); - }); - } - - public void addFocusChangeListener(FocusChangeListener fcl) { - listeners.add(fcl); - } - - private void resetFocus(Node n) { - n.getEventDispatcher().dispatchEvent(new Event(ControlType.NOT_FOCUSED), NO_OP_CHAIN); - if(n instanceof StoredTag) { - listeners.forEach((listener) -> { - listener.focusChanged(new FocusChangeEvent(this, ControlType.NOT_FOCUSED, (StoredTag) n)); - }); - } - } - - public void deleteNode(Node n) { - if (lastFocus == n) { - resetFocus(n); - lastFocus = null; - } - n.getEventDispatcher().dispatchEvent(new Event(ControlType.DELETE), NO_OP_CHAIN); - this.getChildren().remove(n); - } - - public void requestFocus(Node n) { - if (lastFocus == n) { - return; - } else if (lastFocus != null && lastFocus != n) { - resetFocus(lastFocus); - } - - n.getEventDispatcher().dispatchEvent(new Event(ControlType.FOCUSED), NO_OP_CHAIN); - if(n instanceof StoredTag) { - listeners.forEach((listener) -> { - listener.focusChanged(new FocusChangeEvent(this, ControlType.FOCUSED, (StoredTag) n)); - }); - } - - if (n != baseImage) { - n.toFront(); - } - - lastFocus = n; - } -} From fd894a71033fc3b2db2ad28a8a7421a0d3689051 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 3 Jun 2019 13:39:37 -0400 Subject: [PATCH 022/106] 5123 change spot I missed show to download change --- Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java index 250f2ba3cd..b53b1dc258 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java @@ -168,7 +168,7 @@ final class HtmlPanel extends javax.swing.JPanel { * Refresh the panel to reflect the current show/hide images setting. */ @Messages({ - "HtmlPanel_showImagesToggleButton_show=Show Images", + "HtmlPanel_showImagesToggleButton_show=Download Images", "HtmlPanel_showImagesToggleButton_hide=Hide Images", "Html_text_display_error=The HTML text cannot be displayed, it may not be correctly formed HTML.",}) private void refresh() { From 476b407c3c71eed72baab2e29efd10abc8245c0c Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 3 Jun 2019 13:47:19 -0400 Subject: [PATCH 023/106] 5123 update to merged properties file --- .../sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index 7600a880a2..7b8fa6ad6b 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -22,7 +22,7 @@ GstVideoPanel.cannotProcFile.err=The media player cannot process this file. GstVideoPanel.noOpenCase.errMsg=No open case available. Html_text_display_error=The HTML text cannot be displayed, it may not be correctly formed HTML. HtmlPanel_showImagesToggleButton_hide=Hide Images -HtmlPanel_showImagesToggleButton_show=Show Images +HtmlPanel_showImagesToggleButton_show=Download Images HtmlViewer_file_error=This file is missing or unreadable. MediaFileViewer.initGst.gstException.msg=Error initializing gstreamer for audio/video viewing and frame extraction capabilities. Video and audio viewing will be disabled. GstVideoPanel.setupVideo.infoLabel.text=Playback of deleted videos is not supported, use an external player. From 057fa9d48cd0e8acf0830f873d6cdae05c856bd5 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Mon, 3 Jun 2019 14:01:23 -0400 Subject: [PATCH 024/106] Bug fixes, comment fixes, more code clean up --- .../ContentViewerTagManager.java | 32 +++++++++---------- .../autopsy/contentviewers/Bundle.properties | 1 - .../contentviewers/Bundle.properties-MERGED | 1 - .../contentviewers/MediaViewImagePanel.form | 11 ------- .../contentviewers/MediaViewImagePanel.java | 32 +++++++------------ .../contentviewers/imagetagging/ImageTag.java | 8 ++--- .../imagetagging/ImageTagsGroup.java | 10 +++--- 7 files changed, 35 insertions(+), 60 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java index 33b50bab07..105a643331 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java @@ -37,7 +37,7 @@ import org.sleuthkit.datamodel.TskCoreException; public class ContentViewerTagManager { //Used to convert Java beans into the physical representation that will be stored - //in the database (for now, JSON was chosen). + //in the database. private static final ObjectMapper SERIALIZER = new ObjectMapper(); public static final String TABLE_NAME = "beta_tag_app_data"; @@ -53,20 +53,19 @@ public class ContentViewerTagManager { /** * Creates and saves a new ContentViewerTag in the case database. The * generic tag data instance T will be automatically serialized into a - * storable format and persisted. + * storable format. * - * @param Generic java bean class type that will be serialized into a - * storable format for persistence + * @param Generic class type that will be serialized into a + * storable format for persistence. * @param contentTag ContentTag that this ContentViewerTag is associated - * with (1:1) + * with (1:1). * @param tagDataBean Data instance that contains the tag information to be * persisted. * @return An instance of a ContentViewerTag of type T, which contains all * the stored information. * - * @throws SerializationException Thrown if the tag data bean instance T - * could not be serialized into a storable format for persistence in the - * case database. + * @throws SerializationException Thrown if the tag data instance T + * could not be serialized into a storable format. * @throws TskCoreException Thrown if this operation did not successfully * persist in the case database. * @throws NoCurrentCaseException Thrown if invocation of this method occurs @@ -89,15 +88,14 @@ public class ContentViewerTagManager { * Updates the ContentViewerTag instance with the new tag data T and * persists the changes to the case database. * - * @param Generic java bean class type that will be serialized into a - * storable format for persistence + * @param Generic class type that will be serialized into a + * storable format. * @param oldTag ContentViewerTag instance to be updated * @param tagDataBean Data instance that contains the updated information to * be persisted. * - * @throws SerializationException Thrown if the tag data bean instance T - * could not be serialized into a storable format for persistence in the - * case database. + * @throws SerializationException Thrown if the tag data instance T + * could not be serialized into a storable format. * @throws TskCoreException Thrown if this operation did not successfully * persist in the case database. * @throws NoCurrentCaseException Thrown if invocation of this method occurs @@ -119,15 +117,15 @@ public class ContentViewerTagManager { /** * Retrieves a ContentViewerTag instance that is associated with the - * specified ContentTag. The Java bean class that represents the technical + * specified ContentTag. The Java class T that represents the technical * details of the tag should be passed so that automatic binding can take * place. * - * @param Java bean class type that will be instantiated and filled in + * @param Generic class type that will be instantiated and filled in * with data. * @param contentTag ContentTag that this ContentViewerTag is associated * with (1:1) - * @param clazz Java bean class that will be instantiated and filled in with + * @param clazz Generic class that will be instantiated and filled in with * data. * @return ContentViewerTag with an instance of T as a member variable or * null if the content tag does not have an associated ContentViewerTag of @@ -187,7 +185,7 @@ public class ContentViewerTagManager { * for the tag id, the attached Content tag object, and the Java bean * instance that describes the technical details for reconstructing the tag. * - * @param Java bean class type that will be instantiated and filled in + * @param Generic class type that will be instantiated and filled in * with data. */ public static class ContentViewerTag { diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index a8b66e1c31..0f5f1e841e 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -91,5 +91,4 @@ MediaPlayerPanel.playButton.text=\u25ba MediaPlayerPanel.infoLabel.text=No Errors MediaViewImagePanel.deleteTagButton.text=Delete Tag MediaViewImagePanel.createTagButton.text=Create Tag -MediaViewImagePanel.jMenu1.text=jMenu1 MediaViewImagePanel.showTagsButton.text=Hide Tags diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index 44cae34fd9..f04db7f709 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -153,7 +153,6 @@ MediaPlayerPanel.playButton.text=\u25ba MediaPlayerPanel.infoLabel.text=No Errors MediaViewImagePanel.deleteTagButton.text=Delete Tag MediaViewImagePanel.createTagButton.text=Create Tag -MediaViewImagePanel.jMenu1.text=jMenu1 MediaViewImagePanel.showTagsButton.text=Hide Tags # {0} - tableName SQLiteViewer.readTable.errorText=Error getting rows for table: {0} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form index 447957d66c..befc6fe7a6 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form @@ -2,17 +2,6 @@
- - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index 5d6fa7e193..bb921a2974 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -196,7 +196,10 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan if (imageTagCreator != null) { imageTagCreator.disconnect(); } - showTagsButton.setText("Hide Tags"); + showTagsButton.setText(DisplayOptions.HIDE_TAGS.getName()); + showTagsButton.setEnabled(true); + createTagButton.setEnabled(true); + deleteTagButton.setEnabled(false); scrollPane.setContent(null); scrollPane.setContent(masterGroup); }); @@ -347,20 +350,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan * Build the image tag, add an edit event call back to persist all * edits made on this image tag instance. */ - ImageTag imageTag = new ImageTag(contentViewerTag, fxImageView); - imageTag.subscribeToEditEvents((event) -> { - try { - scrollPane.setCursor(Cursor.WAIT); - ImageTagRegion newRegion = (ImageTagRegion) event.getNewValue(); - ContentViewerTagManager.updateTag(contentViewerTag, newRegion); - scrollPane.setCursor(Cursor.DEFAULT); - } catch (SerializationException | NoCurrentCaseException | TskCoreException ex) { - LOGGER.log(Level.WARNING, "Could not save edit for image tag in case db", ex); //NON-NLS - //TODO - pop dialog - } - }); - - tagsGroup.getChildren().add(imageTag); + tagsGroup.getChildren().add(buildImageTag(contentViewerTag)); }); return tagsGroup; @@ -407,7 +397,6 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan // //GEN-BEGIN:initComponents private void initComponents() { - jMenu1 = new javax.swing.JMenu(); jPopupMenu1 = new javax.swing.JPopupMenu(); jPopupMenu2 = new javax.swing.JPopupMenu(); toolbar = new javax.swing.JToolBar(); @@ -428,8 +417,6 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan jSeparator3 = new javax.swing.JToolBar.Separator(); showTagsButton = new javax.swing.JButton(); - org.openide.awt.Mnemonics.setLocalizedText(jMenu1, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.jMenu1.text")); // NOI18N - setBackground(new java.awt.Color(0, 0, 0)); addComponentListener(new java.awt.event.ComponentAdapter() { public void componentResized(java.awt.event.ComponentEvent evt) { @@ -661,6 +648,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan //Persist and build image tag Platform.runLater(() -> { try { + scrollPane.setCursor(Cursor.WAIT); ContentViewerTag contentViewerTag = storeImageTag(tag, result); ImageTag imageTag = buildImageTag(contentViewerTag); tagsGroup.getChildren().add(imageTag); @@ -668,6 +656,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan LOGGER.log(Level.WARNING, "Could not save new image tag in case db", ex); //NON-NLS //TODO pop dialog } + + scrollPane.setCursor(Cursor.DEFAULT); }); }); @@ -683,7 +673,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan }//GEN-LAST:event_createTagButtonActionPerformed /** - * + * Creates an ImageTag instance from the ContentViewerTag. + * * @param contentViewerTag * @return */ @@ -696,11 +687,11 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan scrollPane.setCursor(Cursor.WAIT); ImageTagRegion newRegion = (ImageTagRegion) edit.getNewValue(); ContentViewerTagManager.updateTag(contentViewerTag, newRegion); - scrollPane.setCursor(Cursor.DEFAULT); } catch (SerializationException | TskCoreException | NoCurrentCaseException ex) { LOGGER.log(Level.WARNING, "Could not save edit for image tag in case db", ex); //NON-NLS //TODO pop dialog } + scrollPane.setCursor(Cursor.DEFAULT); }); return imageTag; } @@ -773,7 +764,6 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private javax.swing.JButton deleteTagButton; private javax.swing.Box.Filler filler1; private javax.swing.Box.Filler filler2; - private javax.swing.JMenu jMenu1; private javax.swing.JPopupMenu jPopupMenu1; private javax.swing.JPopupMenu jPopupMenu2; private javax.swing.JToolBar.Separator jSeparator1; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java index e54d365331..80b1cbe07f 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java @@ -54,16 +54,16 @@ public final class ImageTag extends Group { //The underlying presistent tag details that this image tag originates from private final ContentViewerTag appTag; - public ImageTag(ContentViewerTag appTag, ImageView image) { + public ImageTag(ContentViewerTag contentViewerTag, ImageView image) { ALL_CHILDREN = new EventDispatchChainImpl(); - this.appTag = appTag; + this.appTag = contentViewerTag; this.getChildren().addListener((ListChangeListener) change -> { change.next(); change.getAddedSubList().forEach((node) -> ALL_CHILDREN.append(node.getEventDispatcher())); }); - ImageTagRegion details = appTag.getDetails(); + ImageTagRegion details = contentViewerTag.getDetails(); PhysicalTag physicalTag = new PhysicalTag(details); //Defines the max allowable boundary that a user may drag any given handle. @@ -115,7 +115,7 @@ public final class ImageTag extends Group { this.getChildren().addAll(physicalTag, bottomLeft, bottomRight, topLeft, topRight, bottomMiddle, topMiddle, rightMiddle, leftMiddle); - Tooltip.install(this, new Tooltip(appTag.getContentTag() + Tooltip.install(this, new Tooltip(contentViewerTag.getContentTag() .getName().getDisplayName())); this.addEventHandler(ImageTagControls.NOT_FOCUSED, event -> ALL_CHILDREN.dispatchEvent(event)); diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java index 86257e55f8..828b083d80 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java @@ -41,7 +41,7 @@ import javafx.scene.input.MouseEvent; public final class ImageTagsGroup extends Group { private final EventDispatchChainImpl NO_OP_CHAIN = new EventDispatchChainImpl(); - private final PropertyChangeSupport pcl = new PropertyChangeSupport(this); + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private volatile ImageTag currentFocus; @@ -55,7 +55,7 @@ public final class ImageTagsGroup extends Group { currentFocus = null; } - this.pcl.firePropertyChange(new PropertyChangeEvent(this, + this.pcs.firePropertyChange(new PropertyChangeEvent(this, ImageTagControls.NOT_FOCUSED.getName(), currentFocus, null)); }); @@ -81,7 +81,7 @@ public final class ImageTagsGroup extends Group { * @param fcl PCL to be notified which Image tag has been selected. */ public void addFocusChangeListener(PropertyChangeListener fcl) { - this.pcl.addPropertyChangeListener(fcl); + this.pcs.addPropertyChangeListener(fcl); } /** @@ -100,7 +100,7 @@ public final class ImageTagsGroup extends Group { */ private void resetFocus(ImageTag n) { n.getEventDispatcher().dispatchEvent(new Event(ImageTagControls.NOT_FOCUSED), NO_OP_CHAIN); - this.pcl.firePropertyChange(new PropertyChangeEvent(this, ImageTagControls.NOT_FOCUSED.getName(), n, null)); + this.pcs.firePropertyChange(new PropertyChangeEvent(this, ImageTagControls.NOT_FOCUSED.getName(), n, null)); } /** @@ -116,7 +116,7 @@ public final class ImageTagsGroup extends Group { } n.getEventDispatcher().dispatchEvent(new Event(ImageTagControls.FOCUSED), NO_OP_CHAIN); - this.pcl.firePropertyChange(new PropertyChangeEvent(this, ImageTagControls.FOCUSED.getName(), currentFocus, n)); + this.pcs.firePropertyChange(new PropertyChangeEvent(this, ImageTagControls.FOCUSED.getName(), currentFocus, n)); currentFocus = n; n.toFront(); From 64e7e03ec5b7f9c41e35a1eb16fb31096cc75b31 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Mon, 3 Jun 2019 14:04:38 -0400 Subject: [PATCH 025/106] Fixed generic type T warning --- .../services/applicationtags/ContentViewerTagManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java index 105a643331..33d7987d8a 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java @@ -139,7 +139,7 @@ public class ContentViewerTagManager { public static ContentViewerTag getTag(ContentTag contentTag, Class clazz) throws TskCoreException, NoCurrentCaseException { try { String selectTemplateInstance = String.format(SELECT_TAG_DATA, contentTag.getId()); - final ArrayList result = new ArrayList<>(); + final ArrayList> result = new ArrayList<>(); Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager() .select(selectTemplateInstance, (ResultSet rs) -> { try { From 6a6a282e66acece02fa28477177550258be79da4 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Mon, 3 Jun 2019 14:21:56 -0400 Subject: [PATCH 026/106] Added more space for text in buttons --- .../contentviewers/MediaViewImagePanel.form | 21 +++++++++++++++++++ .../contentviewers/MediaViewImagePanel.java | 13 ++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form index befc6fe7a6..fe7843bf7a 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form @@ -250,6 +250,16 @@ + + + + + + + + + + @@ -265,6 +275,17 @@ + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index bb921a2974..1e039d614a 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -268,7 +268,6 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan tagsGroup = buildImageTagsGroup(contentViewerTags); } catch (TskCoreException | NoCurrentCaseException ex) { LOGGER.log(Level.WARNING, "Could not retrieve image tags for file in case db", ex); //NON-NLS - //TODO - pop dialog } scrollPane.setContent(masterGroup); } else { @@ -539,6 +538,10 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan deleteTagButton.setFocusable(false); deleteTagButton.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); deleteTagButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + deleteTagButton.setMaximumSize(new java.awt.Dimension(61, 21)); + deleteTagButton.setMinimumSize(new java.awt.Dimension(61, 21)); + deleteTagButton.setPreferredSize(new java.awt.Dimension(61, 21)); + deleteTagButton.setRequestFocusEnabled(false); deleteTagButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); deleteTagButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -551,6 +554,11 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan org.openide.awt.Mnemonics.setLocalizedText(showTagsButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.showTagsButton.text")); // NOI18N showTagsButton.setFocusable(false); showTagsButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + showTagsButton.setMaximumSize(new java.awt.Dimension(61, 21)); + showTagsButton.setMinimumSize(new java.awt.Dimension(61, 21)); + showTagsButton.setOpaque(false); + showTagsButton.setPreferredSize(new java.awt.Dimension(61, 21)); + showTagsButton.setRolloverEnabled(false); showTagsButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); showTagsButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -618,7 +626,6 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan tagsGroup.getChildren().remove(tagInFocus); } catch (TskCoreException | NoCurrentCaseException ex) { LOGGER.log(Level.WARNING, "Could not delete image tag in case db", ex); //NON-NLS - //TODO pop dialog } scrollPane.setCursor(Cursor.DEFAULT); @@ -654,7 +661,6 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan tagsGroup.getChildren().add(imageTag); } catch (TskCoreException | SerializationException | NoCurrentCaseException ex) { LOGGER.log(Level.WARNING, "Could not save new image tag in case db", ex); //NON-NLS - //TODO pop dialog } scrollPane.setCursor(Cursor.DEFAULT); @@ -689,7 +695,6 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan ContentViewerTagManager.updateTag(contentViewerTag, newRegion); } catch (SerializationException | TskCoreException | NoCurrentCaseException ex) { LOGGER.log(Level.WARNING, "Could not save edit for image tag in case db", ex); //NON-NLS - //TODO pop dialog } scrollPane.setCursor(Cursor.DEFAULT); }); From 37deadd747fd98876312f2e0d8bed949c8195154 Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 3 Jun 2019 15:01:40 -0400 Subject: [PATCH 027/106] Use UTF8 encoding for report content. --- Core/src/org/sleuthkit/autopsy/report/FileReportText.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/FileReportText.java b/Core/src/org/sleuthkit/autopsy/report/FileReportText.java index 9d3de2c945..ea0f3452c2 100644 --- a/Core/src/org/sleuthkit/autopsy/report/FileReportText.java +++ b/Core/src/org/sleuthkit/autopsy/report/FileReportText.java @@ -24,6 +24,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -63,9 +64,12 @@ class FileReportText implements FileReportModule { public void startReport(String baseReportDir) { this.reportPath = baseReportDir + FILE_NAME; try { - out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(this.reportPath))); + out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(this.reportPath), StandardCharsets.UTF_8)); + out.write('\ufeff'); } catch (FileNotFoundException ex) { logger.log(Level.WARNING, "Failed to create report text file", ex); //NON-NLS + } catch (IOException ex) { + logger.log(Level.WARNING, "Failed to write BOM to report text file", ex); //NON-NLS } } From 2cdf0dd66d6328934e61eb87b6e1647ddd9f37e6 Mon Sep 17 00:00:00 2001 From: Raman Date: Mon, 3 Jun 2019 15:45:00 -0400 Subject: [PATCH 028/106] 4963: DataResultTable is slow to load. - load S C & O columns data in background for AbstractContentNode and all it's derived classes. --- .../relationships/MessageNode.java | 11 -- .../datamodel/AbstractAbstractFileNode.java | 94 +++--------- .../datamodel/AbstractContentNode.java | 145 ++++++++++++++++++ .../datamodel/BlackboardArtifactNode.java | 84 ++++++---- .../datamodel/Bundle.properties-MERGED | 1 + .../autopsy/datamodel/GetSCOTask.java | 24 +-- 6 files changed, 234 insertions(+), 125 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java index e6ac3f507f..2f4ce1157a 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java @@ -81,17 +81,6 @@ final class MessageNode extends BlackboardArtifactNode { sheetSet.put(new NodeProperty<>("Type", Bundle.MessageNode_Node_Property_Type(), "", getDisplayName())); //NON-NLS - addScoreProperty(sheetSet, tags); - - CorrelationAttributeInstance correlationAttribute = null; - if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) { - correlationAttribute = getCorrelationAttributeInstance(); - } - addCommentProperty(sheetSet, tags, correlationAttribute); - - if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) { - addCountProperty(sheetSet, correlationAttribute); - } final BlackboardArtifact artifact = getArtifact(); BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index d6b589f977..e8d71caae7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.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,7 +18,6 @@ */ package org.sleuthkit.autopsy.datamodel; -import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.ref.WeakReference; @@ -27,8 +26,6 @@ import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.stream.Collectors; import org.apache.commons.io.FilenameUtils; @@ -65,6 +62,7 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -82,10 +80,6 @@ public abstract class AbstractAbstractFileNode extends A private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE, Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, Case.Events.CR_COMMENT_CHANGED); - // pool to run long running translation and getSCO tasks in backgound - private static final ExecutorService translationSCOPool; - private static final Integer MAX_POOL_SIZE = 10; - /** * @param abstractFile file to wrap */ @@ -102,7 +96,7 @@ public abstract class AbstractAbstractFileNode extends A } if (UserPreferences.displayTranslatedFileNames()) { - AbstractAbstractFileNode.translationSCOPool.submit(new TranslationTask( + backgroundTasksPool.submit(new TranslationTask( new WeakReference<>(this), weakPcl)); } @@ -110,14 +104,7 @@ public abstract class AbstractAbstractFileNode extends A // or when tags are added. Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl); } - - static { - //Initialize this pool only once! This will be used by every instance of AAFN - //to do their heavy duty SCO column and translation updates. - translationSCOPool = Executors.newFixedThreadPool(MAX_POOL_SIZE, - new ThreadFactoryBuilder().setNameFormat("translation-and-sco-task-thread-%d").build()); - } - + /** * The finalizer removes event listeners as the BlackboardArtifactNode is * being garbage collected. Yes, we know that finalizers are considered to @@ -138,17 +125,7 @@ public abstract class AbstractAbstractFileNode extends A Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl); } - /** - * Event signals to indicate the background tasks have completed processing. - * Currently, we have one property task in the background: - * - * 1) Retreiving the translation of the file name - */ - enum NodeSpecificEvents { - TRANSLATION_AVAILABLE, - SCO_AVAILABLE - } - + private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { String eventType = evt.getPropertyName(); @@ -192,7 +169,7 @@ public abstract class AbstractAbstractFileNode extends A } else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) { ContentTagAddedEvent event = (ContentTagAddedEvent) evt; if (event.getAddedTag().getContent().equals(content)) { - List tags = getContentTagsFromDatabase(); + List tags = this.getAllTagsFromDatabase(); Pair scorePropAndDescr = getScorePropertyAndDescription(tags); Score value = scorePropAndDescr.getLeft(); String descr = scorePropAndDescr.getRight(); @@ -204,7 +181,7 @@ public abstract class AbstractAbstractFileNode extends A } else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) { ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt; if (event.getDeletedTagInfo().getContentID() == content.getId()) { - List tags = getContentTagsFromDatabase(); + List tags = getAllTagsFromDatabase(); Pair scorePropAndDescr = getScorePropertyAndDescription(tags); Score value = scorePropAndDescr.getLeft(); String descr = scorePropAndDescr.getRight(); @@ -216,7 +193,7 @@ public abstract class AbstractAbstractFileNode extends A } else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) { CommentChangedEvent event = (CommentChangedEvent) evt; if (event.getContentID() == content.getId()) { - List tags = getContentTagsFromDatabase(); + List tags = getAllTagsFromDatabase(); CorrelationAttributeInstance attribute = getCorrelationAttributeInstance(); updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, getCommentProperty(tags, attribute))); } @@ -249,38 +226,6 @@ public abstract class AbstractAbstractFileNode extends A */ private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null); - /** - * Updates the values of the properties in the current property sheet with - * the new properties being passed in. Only if that property exists in the - * current sheet will it be applied. That way, we allow for subclasses to - * add their own (or omit some!) properties and we will not accidentally - * disrupt their UI. - * - * Race condition if not synchronized. Only one update should be applied at - * a time. - * - * @param newProps New file property instances to be updated in the current - * sheet. - */ - private synchronized void updateSheet(NodeProperty... newProps) { - //Refresh ONLY those properties in the sheet currently. Subclasses may have - //only added a subset of our properties or their own props.s - Sheet visibleSheet = this.getSheet(); - Sheet.Set visibleSheetSet = visibleSheet.get(Sheet.PROPERTIES); - Property[] visibleProps = visibleSheetSet.getProperties(); - for (NodeProperty newProp : newProps) { - for (int i = 0; i < visibleProps.length; i++) { - if (visibleProps[i].getName().equals(newProp.getName())) { - visibleProps[i] = newProp; - } - } - } - visibleSheetSet.put(visibleProps); - visibleSheet.put(visibleSheetSet); - //setSheet() will notify Netbeans to update this node in the UI. - this.setSheet(visibleSheet); - } - /* * This is called when the node is first initialized. Any new updates or * changes happen by directly manipulating the sheet. That means we can fire @@ -389,7 +334,7 @@ public abstract class AbstractAbstractFileNode extends A // Get the SCO columns data in a background task - AbstractAbstractFileNode.translationSCOPool.submit(new GetSCOTask( + backgroundTasksPool.submit(new GetSCOTask( new WeakReference<>(this), weakPcl)); properties.add(new NodeProperty<>(LOCATION.toString(), LOCATION.toString(), NO_DESCR, getContentPath(content))); @@ -453,7 +398,8 @@ public abstract class AbstractAbstractFileNode extends A "AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated", "# {0} - occuranceCount", "AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"}) - Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) { + @Override + protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) { Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting String description = Bundle.AbstractAbstractFileNode_createSheet_count_noCentralRepo_description(); try { @@ -480,7 +426,8 @@ public abstract class AbstractAbstractFileNode extends A "AbstractAbstractFileNode.createSheet.taggedFile.description=File has been tagged.", "AbstractAbstractFileNode.createSheet.notableTaggedFile.description=File tagged with notable tag.", "AbstractAbstractFileNode.createSheet.noScore.description=No score"}) - Pair getScorePropertyAndDescription(List tags) { + @Override + protected Pair getScorePropertyAndDescription(List tags) { DataResultViewerTable.Score score = DataResultViewerTable.Score.NO_SCORE; String description = Bundle.AbstractAbstractFileNode_createSheet_noScore_description(); if (content.getKnown() == TskData.FileKnown.BAD) { @@ -498,7 +445,7 @@ public abstract class AbstractAbstractFileNode extends A if (!tags.isEmpty() && (score == DataResultViewerTable.Score.NO_SCORE || score == DataResultViewerTable.Score.INTERESTING_SCORE)) { score = DataResultViewerTable.Score.INTERESTING_SCORE; description = Bundle.AbstractAbstractFileNode_createSheet_taggedFile_description(); - for (ContentTag tag : tags) { + for (Tag tag : tags) { if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) { score = DataResultViewerTable.Score.NOTABLE_SCORE; description = Bundle.AbstractAbstractFileNode_createSheet_notableTaggedFile_description(); @@ -511,11 +458,12 @@ public abstract class AbstractAbstractFileNode extends A @NbBundle.Messages({ "AbstractAbstractFileNode.createSheet.comment.displayName=C"}) - HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { + @Override + protected HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { DataResultViewerTable.HasCommentStatus status = !tags.isEmpty() ? DataResultViewerTable.HasCommentStatus.TAG_NO_COMMENT : DataResultViewerTable.HasCommentStatus.NO_COMMENT; - for (ContentTag tag : tags) { + for (Tag tag : tags) { if (!StringUtils.isBlank(tag.getComment())) { //if the tag is null or empty or contains just white space it will indicate there is not a comment status = DataResultViewerTable.HasCommentStatus.TAG_COMMENT; @@ -583,7 +531,13 @@ public abstract class AbstractAbstractFileNode extends A return tags; } - CorrelationAttributeInstance getCorrelationAttributeInstance() { + @Override + protected List getAllTagsFromDatabase() { + return new ArrayList<>(getContentTagsFromDatabase()); + } + + @Override + protected CorrelationAttributeInstance getCorrelationAttributeInstance() { CorrelationAttributeInstance attribute = null; if (EamDb.isEnabled() && !UserPreferences.hideCentralRepoCommentsAndOccurrences()) { attribute = EamArtifactUtil.getInstanceFromContent(content); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index 4f6f2e5f47..7a2577e407 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -18,20 +18,30 @@ */ package org.sleuthkit.autopsy.datamodel; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.logging.Level; +import org.apache.commons.lang3.tuple.Pair; import org.openide.nodes.Children; +import org.openide.nodes.Sheet; import org.openide.util.lookup.Lookups; import org.openide.util.Lookup; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskException; @@ -50,6 +60,34 @@ public abstract class AbstractContentNode extends ContentNode T content; private static final Logger logger = Logger.getLogger(AbstractContentNode.class.getName()); + /** + * A pool of background tasks to run any long computation needed to + * populate this node. + */ + static final ExecutorService backgroundTasksPool; + static final Integer MAX_POOL_SIZE = 10; + + @NbBundle.Messages("AbstractContentNode.nodescription=no description") + private static final String NO_DESCR = Bundle.AbstractContentNode_nodescription(); + + + /** + * Event signals to indicate the background tasks have completed processing. + * Currently, we have one property task in the background: + * + * 1) Retrieving the translation of the file name + */ + enum NodeSpecificEvents { + TRANSLATION_AVAILABLE, + SCO_AVAILABLE + } + + static { + //Initialize this pool only once! This will be used by every instance of AAFN + //to do their heavy duty SCO column and translation updates. + backgroundTasksPool = Executors.newFixedThreadPool(MAX_POOL_SIZE, + new ThreadFactoryBuilder().setNameFormat("content-node-background-task-%d").build()); + } /** * Handles aspects that depend on the Content object * @@ -240,4 +278,111 @@ public abstract class AbstractContentNode extends ContentNode public int read(byte[] buf, long offset, long len) throws TskException { return content.read(buf, offset, len); } + + + /** + * Updates the values of the properties in the current property sheet with + * the new properties being passed in. Only if that property exists in the + * current sheet will it be applied. That way, we allow for subclasses to + * add their own (or omit some!) properties and we will not accidentally + * disrupt their UI. + * + * Race condition if not synchronized. Only one update should be applied at + * a time. + * + * @param newProps New file property instances to be updated in the current + * sheet. + */ + protected synchronized void updateSheet(NodeProperty... newProps) { + //Refresh ONLY those properties in the sheet currently. Subclasses may have + //only added a subset of our properties or their own props.s + Sheet visibleSheet = this.getSheet(); + Sheet.Set visibleSheetSet = visibleSheet.get(Sheet.PROPERTIES); + Property[] visibleProps = visibleSheetSet.getProperties(); + for (NodeProperty newProp : newProps) { + for (int i = 0; i < visibleProps.length; i++) { + if (visibleProps[i].getName().equals(newProp.getName())) { + visibleProps[i] = newProp; + } + } + } + visibleSheetSet.put(visibleProps); + visibleSheet.put(visibleSheetSet); + //setSheet() will notify Netbeans to update this node in the UI. + this.setSheet(visibleSheet); + } + + /** + * Reads and returns a list of all tags associated with this content node. + * + * This default implementation returns an empty list. + * The derived classes should override with an implementation specific + * to the type of node. + * + * @return list of tags associated with the node. + */ + protected List getAllTagsFromDatabase() { + return new ArrayList<>(); + } + /** + * Returns correlation attribute instance for the underlying content of the node. + * + * This default implementation returns null. + * The derived classes should override with an implementation specific + * to the type of node. + * + * @return correlation attribute instance for the underlying content of the node. + * + */ + protected CorrelationAttributeInstance getCorrelationAttributeInstance() { + return null; + } + + /** + * Returns Score property for the node. + * + * This default implementation returns NO_SCORE. + * The derived classes should override with an implementation specific + * to the type of node. + * + * @param tags list of tags. + * + * @return Score property for the underlying content of the node. + * + */ + protected Pair getScorePropertyAndDescription(List tags) { + + return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR); + } + /** + * Returns comment property for the node. + * + * This default implementation returns NO_COMMENT. + * The derived classes should override with an implementation specific + * to the type of node. + * + * @param tags list of tags + * @param attribute correlation attribute instance + * + * @return Comment property for the underlying content of the node. + * + */ + protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { + return DataResultViewerTable.HasCommentStatus.NO_COMMENT; + } + /** + * Returns occurrences/count property for the node. + * + * This default implementation returns -1. + * The derived classes should override with an implementation specific + * to the type of node. + * + * @param attribute correlation attribute instance + * + * @return count property for the underlying content of the node. + * + */ + protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) { + return Pair.of(-1L, NO_DESCR); + } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 55e81acd17..1f871363f9 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.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 com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.lang.ref.WeakReference; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; @@ -37,6 +38,7 @@ import java.util.logging.Level; import java.util.stream.Collectors; import javax.swing.Action; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.openide.nodes.Sheet; import org.openide.util.Lookup; import org.openide.util.NbBundle; @@ -55,11 +57,13 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus; +import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager; import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction; import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; @@ -96,7 +100,7 @@ public class BlackboardArtifactNode extends AbstractContentNode> customProperties; - private final static String NO_DESCR = NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.noDesc.text"); + protected final static String NO_DESCR = NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.noDesc.text"); /* @@ -151,6 +155,19 @@ public class BlackboardArtifactNode extends AbstractContentNode(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoData.getScoreAndDescription().getRight(), scoData.getScoreAndDescription().getLeft())); + } + if (scoData.getComment() != null) { + updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, scoData.getComment())); + } + if (scoData.getCountAndDescription() != null && + !UserPreferences.hideCentralRepoCommentsAndOccurrences()) { + updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft())); + } + } } }; @@ -335,8 +352,6 @@ public class BlackboardArtifactNode extends AbstractContentNode tags = getAllTagsFromDatabase(); - Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); if (sheetSet == null) { sheetSet = Sheet.createPropertiesSet(); @@ -351,17 +366,15 @@ public class BlackboardArtifactNode extends AbstractContentNode(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), NO_DESCR, "")); + + // Get the SCO columns data in a background task + backgroundTasksPool.submit(new GetSCOTask( + new WeakReference<>(this), weakPcl)); + if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) { try { BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); @@ -520,6 +533,7 @@ public class BlackboardArtifactNode extends AbstractContentNode getAllTagsFromDatabase() { List tags = new ArrayList<>(); try { @@ -569,6 +583,7 @@ public class BlackboardArtifactNode extends AbstractContentNode t.getName().getDisplayName()).collect(Collectors.joining(", ")))); } + @Override protected final CorrelationAttributeInstance getCorrelationAttributeInstance() { CorrelationAttributeInstance correlationAttribute = null; if (EamDb.isEnabled()) { @@ -581,16 +596,17 @@ public class BlackboardArtifactNode extends AbstractContentNode tags, CorrelationAttributeInstance attribute) { + @Override + protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { + HasCommentStatus status = tags.size() > 0 ? HasCommentStatus.TAG_NO_COMMENT : HasCommentStatus.NO_COMMENT; for (Tag tag : tags) { if (!StringUtils.isBlank(tag.getComment())) { @@ -609,17 +625,16 @@ public class BlackboardArtifactNode extends AbstractContentNode(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, - status)); + return status; } - + /** * Used by (subclasses of) BlackboardArtifactNode to add the Score property * to their sheets. * - * @param sheetSet the modifiable Sheet.Set returned by - * Sheet.get(Sheet.PROPERTIES) * @param tags the list of tags associated with the file + * + * @return score property */ @NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S", "BlackboardArtifactNode.createSheet.score.displayName=S", @@ -628,8 +643,10 @@ public class BlackboardArtifactNode extends AbstractContentNode tags) { - Score score = Score.NO_SCORE; + + @Override + protected Pair getScorePropertyAndDescription(List tags) { + Score score = Score.NO_SCORE; String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description(); if (associated instanceof AbstractFile) { if (((AbstractFile) associated).getKnown() == TskData.FileKnown.BAD) { @@ -673,9 +690,10 @@ public class BlackboardArtifactNode extends AbstractContentNode(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), description, score)); + + return Pair.of(score, description); } - + @NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O", "BlackboardArtifactNode.createSheet.count.displayName=O", "BlackboardArtifactNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated", @@ -683,7 +701,8 @@ public class BlackboardArtifactNode extends AbstractContentNode getCountPropertyAndDescription(CorrelationAttributeInstance attribute) { Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting String description = Bundle.BlackboardArtifactNode_createSheet_count_noCentralRepo_description(); try { @@ -699,14 +718,13 @@ public class BlackboardArtifactNode extends AbstractContentNode(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), description, count)); - } - + return Pair.of(count, description); + } + private void updateSheet() { this.setSheet(createSheet()); } - + private String getRootParentName() { String parentName = associated.getName(); Content parent = associated; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED index 1d8fb7eb14..8d2bd5952d 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED @@ -37,6 +37,7 @@ AbstractAbstractFileNode.tagsProperty.displayName=Tags AbstractAbstractFileNode.typeDirColLbl=Type(Dir) AbstractAbstractFileNode.typeMetaColLbl=Type(Meta) AbstractAbstractFileNode.useridColLbl=UserID +AbstractContentNode.nodescription=no description AbstractFsContentNode.noDesc.text=no description ArtifactStringContent.attrsTableHeader.sources=Source(s) ArtifactStringContent.attrsTableHeader.type=Type diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java index e8a7b5b1f5..4278f99d66 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java @@ -25,42 +25,44 @@ import java.util.List; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.events.AutopsyEvent; -import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.Tag; /** - * Background task to get Score, Comment and Occurrences values for a Abstract file node. + * Background task to get Score, Comment and Occurrences values for an + * Abstract content node. * */ class GetSCOTask implements Runnable { - private final WeakReference> weakNodeRef; + private final WeakReference> weakNodeRef; private final PropertyChangeListener listener; - public GetSCOTask(WeakReference> weakContentRef, PropertyChangeListener listener) { + public GetSCOTask(WeakReference> weakContentRef, PropertyChangeListener listener) { this.weakNodeRef = weakContentRef; this.listener = listener; } @Override public void run() { - AbstractAbstractFileNode fileNode = weakNodeRef.get(); + AbstractContentNode contentNode = weakNodeRef.get(); //Check for stale reference - if (fileNode == null) { + if (contentNode == null) { return; } // get the SCO column values - List tags = fileNode.getContentTagsFromDatabase(); - CorrelationAttributeInstance attribute = fileNode.getCorrelationAttributeInstance(); + List tags = contentNode.getAllTagsFromDatabase(); + CorrelationAttributeInstance attribute = contentNode.getCorrelationAttributeInstance(); SCOData scoData = new SCOData(); - scoData.setScoreAndDescription(fileNode.getScorePropertyAndDescription(tags)); - scoData.setComment(fileNode.getCommentProperty(tags, attribute)); + scoData.setScoreAndDescription(contentNode.getScorePropertyAndDescription(tags)); + scoData.setComment(contentNode.getCommentProperty(tags, attribute)); if (!UserPreferences.hideCentralRepoCommentsAndOccurrences()) { - scoData.setCountAndDescription(fileNode.getCountPropertyAndDescription(attribute)); + scoData.setCountAndDescription(contentNode.getCountPropertyAndDescription(attribute)); } + // signal SCO data is available. if (listener != null) { listener.propertyChange(new PropertyChangeEvent( AutopsyEvent.SourceType.LOCAL.toString(), From 2a37021c809da9f976a5cab47d158de350361f24 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 3 Jun 2019 18:35:28 -0400 Subject: [PATCH 029/106] 5092 working changes to update status in table --- .../datasourcesummary/DataSourceBrowser.java | 42 ++++++++++++++++++- .../DataSourceSummaryDialog.java | 11 +++++ .../DataSourceSummaryNode.java | 6 ++- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java index e946317da8..9c73ddadda 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java @@ -37,7 +37,10 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.datasourcesummary.DataSourceSummaryNode.DataSourceSummaryEntryNode; import static javax.swing.SwingConstants.RIGHT; +import javax.swing.SwingUtilities; import javax.swing.table.TableColumn; +import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.IngestJobInfo; import org.sleuthkit.datamodel.SleuthkitCase; @@ -58,7 +61,7 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana private final Outline outline; private final org.openide.explorer.view.OutlineView outlineView; private final ExplorerManager explorerManager; - private final List dataSourceSummaryList; + private List dataSourceSummaryList; private final RightAlignedTableCellRenderer rightAlignedRenderer = new RightAlignedTableCellRenderer(); /** @@ -187,6 +190,43 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana return null; } + void refresh(long jobId, IngestJobInfo.IngestJobStatusType newStatus) { + Node[] selectedNodes = explorerManager.getSelectedNodes(); + //attempt to update the status of any datasources that had status which was STARTED + for (DataSourceSummary summary : dataSourceSummaryList) { + //attempt to update the status of any datasources that had status which was STARTED + //the database may not have been updated when this event is received so we need to manually update the UI + if (summary.getIngestStatus() == IngestJobInfo.IngestJobStatusType.STARTED && summary.getJobId() == jobId) { + System.out.println("UPDATING STATUS"); + summary.setStatus(newStatus); + } + } + SwingUtilities.invokeLater(() -> { + explorerManager.setRootContext(new DataSourceSummaryNode(dataSourceSummaryList)); + List nodesToSelect = new ArrayList<>(); + for (Node node : explorerManager.getRootContext().getChildren().getNodes()) { + if (node instanceof DataSourceSummaryEntryNode) { + //there should only be one selected node as multi-select is disabled + for (Node selectedNode : selectedNodes) { + if (((DataSourceSummaryEntryNode) node).getDataSource().equals(((DataSourceSummaryEntryNode) selectedNode).getDataSource())) { + System.out.println("NODE TO SELECT ADDED"); + nodesToSelect.add(node); + } + } + } + } + //reselect the previously selected Nodes + try { + explorerManager.setSelectedNodes(nodesToSelect.toArray(new Node[nodesToSelect.size()])); + } catch (PropertyVetoException ex) { + logger.log(Level.WARNING, "Error selecting previously selected nodes", ex); + } + revalidate(); + repaint(); + + }); + } + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java index 3fd86c8de8..91ad7da792 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.awt.Frame; +import java.beans.PropertyChangeEvent; import java.util.Map; import java.util.Observable; import java.util.Observer; @@ -26,7 +27,9 @@ import java.util.logging.Logger; import javax.swing.event.ListSelectionEvent; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; +import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.IngestJobInfo; /** * Dialog for displaying the Data Sources Summary information @@ -73,6 +76,14 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser this.repaint(); } }); + IngestManager.getInstance().addIngestJobEventListener((PropertyChangeEvent evt) -> { + if (evt.getPropertyName().equals(IngestManager.IngestJobEvent.CANCELLED.toString())){ + dataSourcesPanel.refresh((long)evt.getOldValue(), null); + } + else if (evt.getPropertyName().equals(IngestManager.IngestJobEvent.COMPLETED.toString())) { + dataSourcesPanel.refresh((long)evt.getOldValue(), IngestJobInfo.IngestJobStatusType.COMPLETED); + } + }); this.pack(); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java index c9aef8c644..6c5a212d9d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Observable; import java.util.Observer; import javax.swing.Action; @@ -125,7 +126,7 @@ final class DataSourceSummaryNode extends AbstractNode { DataSourceSummaryEntryNode(DataSourceSummary dataSourceSummary) { super(Children.LEAF); dataSource = dataSourceSummary.getDataSource(); - status = dataSourceSummary.getIngestStatus(); + status = dataSourceSummary.getIngestStatus() == null ? "" : dataSourceSummary.getIngestStatus().toString(); type = dataSourceSummary.getType(); filesCount = dataSourceSummary.getFilesCount(); resultsCount = dataSourceSummary.getResultsCount(); @@ -183,7 +184,8 @@ final class DataSourceSummaryNode extends AbstractNode { } @Override - public Action[] getActions(boolean context) { + public Action[] getActions(boolean context + ) { List actions = new ArrayList<>(); actions.add(new ViewDataSourceInContextAction()); return actions.toArray(new Action[actions.size()]); From fb17d40ca49cd1e1c0f8300f1a2d901684d4f632 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 3 Jun 2019 18:40:46 -0400 Subject: [PATCH 030/106] 5092 query for status --- .../datasourcesummary/DataSourceSummary.java | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java index a04604b3cd..a3de258fe8 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java @@ -20,13 +20,10 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.List; -import org.openide.util.Exceptions; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.CaseDbAccessManager; import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.IngestJobInfo; import org.sleuthkit.datamodel.IngestJobInfo.IngestJobStatusType; import org.sleuthkit.datamodel.TskCoreException; @@ -37,7 +34,8 @@ import org.sleuthkit.datamodel.TskCoreException; class DataSourceSummary { private final DataSource dataSource; - private String status = ""; + private IngestJobStatusType status = null; + private Long jobId = null; private final String type; private final long filesCount; private final long resultsCount; @@ -56,23 +54,29 @@ class DataSourceSummary { */ DataSourceSummary(DataSource dSource, String typeValue, Long numberOfFiles, Long numberOfResults, Long numberOfTags) { dataSource = dSource; - updateStatus(); + getStatusFromDatabase(); type = typeValue == null ? "" : typeValue; filesCount = numberOfFiles == null ? 0 : numberOfFiles; resultsCount = numberOfResults == null ? 0 : numberOfResults; tagsCount = numberOfTags == null ? 0 : numberOfTags; } - void updateStatus() { + private void getStatusFromDatabase() { try { IngestJobQueryCallback callback = new IngestJobQueryCallback(); - Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select("status_id FROM ingest_jobs WHERE obj_id=" + dataSource.getId(), callback); + Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select("ingest_job_id, status_id FROM ingest_jobs WHERE obj_id=" + dataSource.getId(), callback); status = callback.getStatus(); + jobId = callback.getJobId(); + System.out.println("NEW STATUS: " + status); } catch (NoCurrentCaseException | TskCoreException ex) { } } + void setStatus(IngestJobStatusType newStatus){ + status = newStatus; + } + /** * Get the DataSource * @@ -82,6 +86,10 @@ class DataSourceSummary { return dataSource; } + Long getJobId() { + return jobId; + } + /** * Get the type of this DataSource * @@ -109,10 +117,10 @@ class DataSourceSummary { return resultsCount; } - String getIngestStatus(){ + IngestJobStatusType getIngestStatus() { return status; } - + /** * Get the number of tagged content objects in this DataSource * @@ -124,16 +132,18 @@ class DataSourceSummary { class IngestJobQueryCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { - IngestJobStatusType jobStatus = null; + private IngestJobStatusType jobStatus = null; + private Long ingestJobId = null; @Override public void process(ResultSet rs) { try { while (rs.next()) { IngestJobStatusType currentStatus = IngestJobStatusType.fromID(rs.getInt("status_id")); - if (currentStatus == IngestJobStatusType.COMPLETED) { + if (currentStatus == IngestJobStatusType.COMPLETED) { jobStatus = currentStatus; - } else if (currentStatus == IngestJobStatusType.STARTED) { + } else if (currentStatus == IngestJobStatusType.STARTED) { + ingestJobId = rs.getLong("ingest_job_id"); jobStatus = currentStatus; return; } @@ -143,12 +153,12 @@ class DataSourceSummary { } } - String getStatus() { - if (jobStatus == null) { - return ""; - } else { - return jobStatus.getDisplayName(); - } + IngestJobStatusType getStatus() { + return jobStatus; + } + + Long getJobId() { + return ingestJobId; } } } From ea6687c2c596939dc4e0bc40d022da947f91d543 Mon Sep 17 00:00:00 2001 From: Raman Date: Tue, 4 Jun 2019 08:20:40 -0400 Subject: [PATCH 031/106] Address Codacy comments. --- .../datamodel/AbstractContentNode.java | 53 ++++----------- .../autopsy/datamodel/ImageNode.java | 67 ++++++++++++++++++ .../autopsy/datamodel/VolumeNode.java | 68 +++++++++++++++++++ 3 files changed, 147 insertions(+), 41 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index 7a2577e407..f90a15ac42 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -67,8 +67,11 @@ public abstract class AbstractContentNode extends ContentNode static final ExecutorService backgroundTasksPool; static final Integer MAX_POOL_SIZE = 10; + /** + * Default no description string + */ @NbBundle.Messages("AbstractContentNode.nodescription=no description") - private static final String NO_DESCR = Bundle.AbstractContentNode_nodescription(); + protected static final String NO_DESCR = Bundle.AbstractContentNode_nodescription(); /** @@ -315,74 +318,42 @@ public abstract class AbstractContentNode extends ContentNode /** * Reads and returns a list of all tags associated with this content node. * - * This default implementation returns an empty list. - * The derived classes should override with an implementation specific - * to the type of node. - * * @return list of tags associated with the node. */ - protected List getAllTagsFromDatabase() { - return new ArrayList<>(); - } + abstract protected List getAllTagsFromDatabase(); + /** * Returns correlation attribute instance for the underlying content of the node. * - * This default implementation returns null. - * The derived classes should override with an implementation specific - * to the type of node. - * * @return correlation attribute instance for the underlying content of the node. - * */ - protected CorrelationAttributeInstance getCorrelationAttributeInstance() { - return null; - } + abstract protected CorrelationAttributeInstance getCorrelationAttributeInstance(); /** * Returns Score property for the node. * - * This default implementation returns NO_SCORE. - * The derived classes should override with an implementation specific - * to the type of node. - * * @param tags list of tags. * * @return Score property for the underlying content of the node. - * */ - protected Pair getScorePropertyAndDescription(List tags) { - - return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR); - } + abstract protected Pair getScorePropertyAndDescription(List tags); + /** * Returns comment property for the node. * - * This default implementation returns NO_COMMENT. - * The derived classes should override with an implementation specific - * to the type of node. - * * @param tags list of tags * @param attribute correlation attribute instance * * @return Comment property for the underlying content of the node. - * */ - protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { - return DataResultViewerTable.HasCommentStatus.NO_COMMENT; - } + abstract protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute); + /** * Returns occurrences/count property for the node. * - * This default implementation returns -1. - * The derived classes should override with an implementation specific - * to the type of node. - * * @param attribute correlation attribute instance * * @return count property for the underlying content of the node. - * */ - protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) { - return Pair.of(-1L, NO_DESCR); - } + abstract protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 584a405fcc..b2c80f1322 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -28,12 +28,15 @@ import java.util.EnumSet; import java.util.List; import java.util.logging.Level; import javax.swing.Action; +import org.apache.commons.lang3.tuple.Pair; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor; import org.sleuthkit.autopsy.directorytree.FileSearchAction; @@ -47,6 +50,7 @@ import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.VirtualDirectory; import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException; +import org.sleuthkit.datamodel.Tag; /** * This class is used to represent the "Node" for the image. The children of @@ -250,4 +254,67 @@ public class ImageNode extends AbstractContentNode { } }; + /** + * Reads and returns a list of all tags associated with this content node. + * + * Null implementation of an abstract method. + * + * @return list of tags associated with the node. + */ + @Override + protected List getAllTagsFromDatabase() { + return new ArrayList<>(); + } + /** + * Returns correlation attribute instance for the underlying content of the node. + * + * Null implementation of an abstract method. + * + * @return correlation attribute instance for the underlying content of the node. + */ + @Override + protected CorrelationAttributeInstance getCorrelationAttributeInstance() { + return null; + } + + /** + * Returns Score property for the node. + * + * Null implementation of an abstract method. + * + * @param tags list of tags. + * + * @return Score property for the underlying content of the node. + */ + @Override + protected Pair getScorePropertyAndDescription(List tags) { + return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR); + } + /** + * Returns comment property for the node. + * + * Null implementation of an abstract method. + * + * @param tags list of tags + * @param attribute correlation attribute instance + * + * @return Comment property for the underlying content of the node. + */ + @Override + protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { + return DataResultViewerTable.HasCommentStatus.NO_COMMENT; + } + /** + * Returns occurrences/count property for the node. + * + * Null implementation of an abstract method. + * + * @param attribute correlation attribute instance + * + * @return count property for the underlying content of the node. + */ + @Override + protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) { + return Pair.of(-1L, NO_DESCR); + } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java index d25b50d59f..233d098071 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java @@ -25,10 +25,14 @@ import java.util.EnumSet; import java.util.List; import java.util.logging.Level; import javax.swing.Action; +import org.apache.commons.lang3.tuple.Pair; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.Logger; +import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR; import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException; import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; @@ -39,6 +43,7 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.VirtualDirectory; import org.sleuthkit.datamodel.Volume; import org.sleuthkit.autopsy.directorytree.FileSystemDetailsAction; +import org.sleuthkit.datamodel.Tag; /** * This class is used to represent the "Node" for the volume. Its child is the @@ -212,4 +217,67 @@ public class VolumeNode extends AbstractContentNode { public String getItemType() { return DisplayableItemNode.FILE_PARENT_NODE_KEY; } + /** + * Reads and returns a list of all tags associated with this content node. + * + * Null implementation of an abstract method. + * + * @return list of tags associated with the node. + */ + @Override + protected List getAllTagsFromDatabase() { + return new ArrayList<>(); + } + /** + * Returns correlation attribute instance for the underlying content of the node. + * + * Null implementation of an abstract method. + * + * @return correlation attribute instance for the underlying content of the node. + */ + @Override + protected CorrelationAttributeInstance getCorrelationAttributeInstance() { + return null; + } + + /** + * Returns Score property for the node. + * + * Null implementation of an abstract method. + * + * @param tags list of tags. + * + * @return Score property for the underlying content of the node. + */ + @Override + protected Pair getScorePropertyAndDescription(List tags) { + return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR); + } + /** + * Returns comment property for the node. + * + * Null implementation of an abstract method. + * + * @param tags list of tags + * @param attribute correlation attribute instance + * + * @return Comment property for the underlying content of the node. + */ + @Override + protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { + return DataResultViewerTable.HasCommentStatus.NO_COMMENT; + } + /** + * Returns occurrences/count property for the node. + * + * Null implementation of an abstract method. + * + * @param attribute correlation attribute instance + * + * @return count property for the underlying content of the node. + */ + @Override + protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) { + return Pair.of(-1L, NO_DESCR); + } } From ed4da71c1aabd6d2c12e66de88671bab92b6d5a7 Mon Sep 17 00:00:00 2001 From: Raman Date: Tue, 4 Jun 2019 08:29:37 -0400 Subject: [PATCH 032/106] Remove unused import. --- .../src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index f90a15ac42..154ca68d50 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.datamodel; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; From 1f1eb95699dc23193a1a39a9ae20694f69261366 Mon Sep 17 00:00:00 2001 From: Brian Kjersten Date: Tue, 4 Jun 2019 09:47:51 -0500 Subject: [PATCH 033/106] 5111 Clean up HTML output --- .../texttranslation/translators/GoogleTranslator.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java index 318191e713..3c761af6e1 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java @@ -34,6 +34,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.texttranslation.TextTranslator; import org.sleuthkit.autopsy.texttranslation.TranslationException; @@ -103,6 +104,10 @@ public final class GoogleTranslator implements TextTranslator { // put back the newlines translatedString = translatedString.replaceAll("
", "\n"); + + // With our current settings, Google Translate outputs HTML + // so we need to undo the escape characters. + translatedString = EscapeUtil.unEscapeHtml(translatedString); return translatedString; } catch (Throwable ex) { //Catching throwables because some of this Google Translate code throws throwables From ff9a26a447e0c6ef9b7a49f0f163b6c1760d0523 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Tue, 4 Jun 2019 12:38:39 -0400 Subject: [PATCH 034/106] Adding export CSV action --- .../datamodel/DataModelActionsFactory.java | 11 + .../autopsy/datamodel/DirectoryNode.java | 2 + .../sleuthkit/autopsy/datamodel/FileNode.java | 2 + .../autopsy/datamodel/LayoutFileNode.java | 2 + .../autopsy/datamodel/LocalFileNode.java | 2 + .../autopsy/datamodel/SlackFileNode.java | 2 + .../datamodel/SpecialDirectoryNode.java | 2 + .../directorytree/Bundle.properties-MERGED | 9 + .../directorytree/DataResultFilterNode.java | 1 + .../ExplorerNodeActionVisitor.java | 5 + .../directorytree/ExportCSVAction.java | 298 ++++++++++++++++++ .../keywordsearch/AdHocSearchFilterNode.java | 2 + 12 files changed, 338 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java index 13a0753ca1..bdb4a394a8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java @@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.actions.ReplaceBlackboardArtifactTagAction; import org.sleuthkit.autopsy.actions.ReplaceContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.datamodel.Reports.ReportNode; +import org.sleuthkit.autopsy.directorytree.ExportCSVAction; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; @@ -92,6 +93,7 @@ public class DataModelActionsFactory { } actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { @@ -119,6 +121,7 @@ public class DataModelActionsFactory { actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, slackFileNode)); actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { @@ -155,6 +158,7 @@ public class DataModelActionsFactory { } actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance());// + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { @@ -189,6 +193,7 @@ public class DataModelActionsFactory { } actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { @@ -223,6 +228,7 @@ public class DataModelActionsFactory { } actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { @@ -257,6 +263,7 @@ public class DataModelActionsFactory { } actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { @@ -291,6 +298,7 @@ public class DataModelActionsFactory { } actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { @@ -325,6 +333,7 @@ public class DataModelActionsFactory { } actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { @@ -379,6 +388,7 @@ public class DataModelActionsFactory { } actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { @@ -415,6 +425,7 @@ public class DataModelActionsFactory { } actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); if (isArtifactSource) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java index 97b0fd8216..5a3476f896 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java @@ -28,6 +28,7 @@ import org.openide.util.Utilities; import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; +import org.sleuthkit.autopsy.directorytree.ExportCSVAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.autopsy.directorytree.ViewContextAction; @@ -89,6 +90,7 @@ public class DirectoryNode extends AbstractFsContentNode { actionsList.add(ViewFileInTimelineAction.createViewFileAction(content)); actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(new RunIngestModulesAction(content)); actionsList.add(null); // creates a menu separator diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java index 1695b253b0..7b3a55c20b 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java @@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.directorytree.ExportCSVAction; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; @@ -173,6 +174,7 @@ public class FileNode extends AbstractFsContentNode { actionsList.add(null); // Creates an item separator actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // Creates an item separator actionsList.add(AddContentTagAction.getInstance()); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java index bfd8f7bd91..41ecad0340 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java @@ -29,6 +29,7 @@ import org.openide.util.Utilities; import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; +import org.sleuthkit.autopsy.directorytree.ExportCSVAction; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; @@ -105,6 +106,7 @@ public class LayoutFileNode extends AbstractAbstractFileNode { } actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java index 6d64aeb621..4a62cd9e4f 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java @@ -31,6 +31,7 @@ import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.directorytree.ExportCSVAction; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; @@ -82,6 +83,7 @@ public class LocalFileNode extends AbstractAbstractFileNode { actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/SlackFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/SlackFileNode.java index 49d1b9da54..0e7bce56cc 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/SlackFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/SlackFileNode.java @@ -28,6 +28,7 @@ import org.openide.util.Utilities; import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; +import org.sleuthkit.autopsy.directorytree.ExportCSVAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.autopsy.directorytree.ViewContextAction; @@ -85,6 +86,7 @@ public class SlackFileNode extends AbstractFsContentNode { NbBundle.getMessage(this.getClass(), "SlackFileNode.getActions.viewInNewWin.text"), this)); actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java index 47f616de12..ce5948ce68 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java @@ -25,6 +25,7 @@ import javax.swing.Action; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; +import org.sleuthkit.autopsy.directorytree.ExportCSVAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.FileSearchAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; @@ -61,6 +62,7 @@ public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode visit(final DerivedFile d) { List actionsList = new ArrayList<>(); actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(AddContentTagAction.getInstance()); final Collection selectedFilesList = @@ -166,6 +169,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default visit(final LocalFile d) { List actionsList = new ArrayList<>(); actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(AddContentTagAction.getInstance()); final Collection selectedFilesList = @@ -182,6 +186,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default visit(final org.sleuthkit.datamodel.File d) { List actionsList = new ArrayList<>(); actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(AddContentTagAction.getInstance()); final Collection selectedFilesList = diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java new file mode 100644 index 0000000000..169701a55c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java @@ -0,0 +1,298 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013-2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this content 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.directorytree; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import javax.swing.AbstractAction; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.SwingWorker; +import javax.swing.filechooser.FileNameExtensionFilter; +import org.netbeans.api.progress.ProgressHandle; +import org.openide.util.Cancellable; +import org.openide.util.NbBundle; +import org.openide.util.Utilities; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.coreutils.FileUtil; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor; +import org.sleuthkit.autopsy.datamodel.FileNode; +import org.sleuthkit.datamodel.AbstractFile; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Node; +import org.openide.nodes.Node.PropertySet; +import org.openide.nodes.Node.Property; + +/** + * Exports CSV version of result nodes to a location selected by the user. + */ +public final class ExportCSVAction extends AbstractAction { + + private Logger logger = Logger.getLogger(ExportCSVAction.class.getName()); + + private String userDefinedExportPath; + + // This class is a singleton to support multi-selection of nodes, since + // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every + // node in the array returns a reference to the same action object from Node.getActions(boolean). + private static ExportCSVAction instance; + + public static synchronized ExportCSVAction getInstance() { + if (null == instance) { + instance = new ExportCSVAction(); + } + return instance; + } + + /** + * Private constructor for the action. + */ + @NbBundle.Messages({"ExportCSV.title.text=Export to CSV"}) + private ExportCSVAction() { + super(Bundle.ExportCSV_title_text()); + } + + /** + * Asks user to choose destination, then extracts content to destination + * (recursing on directories). + * + * @param e The action event. + */ + @NbBundle.Messages({ + "# {0} - Output file", + "ExportCSV.actionPerformed.fileExists=File {0} already exists", + "ExportCSV.actionPerformed.noCurrentCase=No open case available"}) + @Override + public void actionPerformed(ActionEvent e) { + + Collection selectedNodes = Utilities.actionsGlobalContext().lookupAll(Node.class); + if (selectedNodes.isEmpty()) { + return; + } + + Node parent = selectedNodes.iterator().next().getParentNode(); + if (parent != null) { + System.out.println("HTML name: " + parent.getHtmlDisplayName()); + System.out.println("Display name: " + parent.getDisplayName()); + System.out.println("Class: " + parent.getClass().getCanonicalName()); + for (PropertySet set : parent.getPropertySets()) { + for (Property prop : set.getProperties()) { + try { + System.out.println(" " + prop.getDisplayName() + " : " + prop.getValue().toString()); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + } + + try { + String fileName = String.format("%1$tY%1$tm%1$te%1$tI%1$tM%1$tS_listing.csv", Calendar.getInstance()); + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setCurrentDirectory(new File(getExportDirectory(Case.getCurrentCaseThrows()))); + fileChooser.setSelectedFile(new File(fileName)); + fileChooser.setFileFilter(new FileNameExtensionFilter("csv file", "csv")); + + int returnVal = fileChooser.showSaveDialog((Component) e.getSource()); + if (returnVal == JFileChooser.APPROVE_OPTION) { + + File selectedFile = fileChooser.getSelectedFile(); + if (!selectedFile.getName().endsWith(".csv")) { // NON-NLS + selectedFile = new File(selectedFile.toString() + ".csv"); // NON-NLS + } + updateExportDirectory(selectedFile.getParent(), Case.getCurrentCaseThrows()); + + if (selectedFile.exists()) { + logger.log(Level.SEVERE, "File {0} already exists", selectedFile.getAbsolutePath()); //NON-NLS + MessageNotifyUtil.Message.info(Bundle.ExportCSV_actionPerformed_fileExists(selectedFile)); + return; + } + + CSVWriter writer = new CSVWriter(selectedNodes, selectedFile); + writer.execute(); + } + } catch (NoCurrentCaseException ex) { + JOptionPane.showMessageDialog((Component) e.getSource(), Bundle.ExportCSV_actionPerformed_noCurrentCase()); + logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS + } + } + + /** + * Get the export directory path. + * + * @param openCase The current case. + * + * @return The export directory path. + */ + private String getExportDirectory(Case openCase) { + String caseExportPath = openCase.getExportDirectory(); + + if (userDefinedExportPath == null) { + return caseExportPath; + } + + File file = new File(userDefinedExportPath); + if (file.exists() == false || file.isDirectory() == false) { + return caseExportPath; + } + + return userDefinedExportPath; + } + + /** + * Update the default export directory. If the directory path matches the + * case export directory, then the directory used will always match the + * export directory of any given case. Otherwise, the path last used will be + * saved. + * + * @param exportPath The export path. + * @param openCase The current case. + */ + private void updateExportDirectory(String exportPath, Case openCase) { + if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) { + userDefinedExportPath = null; + } else { + userDefinedExportPath = exportPath; + } + } + + + /** + * Thread that does the actual extraction work + */ + private class CSVWriter extends SwingWorker { + + private final Logger logger = Logger.getLogger(CSVWriter.class.getName()); + private ProgressHandle progress; + + private final List nodesToExport; + private final File outputFile; + + /** + * Create an instance of the CSVWriter. + * + * @param extractionTasks List of file extraction tasks. + */ + CSVWriter(Collection selectedNodes, File outputFile) { + this.nodesToExport = new ArrayList<>(selectedNodes); + this.outputFile = outputFile; + } + + @NbBundle.Messages({"CSVWriter.progress.extracting=Exporting to CSV file", + "CSVWriter.progress.cancelling=Cancelling"}) + @Override + protected Object doInBackground() throws Exception { + if (nodesToExport.isEmpty()) { + return null; + } + + // Set up progress bar. + final String displayName = Bundle.CSVWriter_progress_extracting(); + progress = ProgressHandle.createHandle(displayName, new Cancellable() { + @Override + public boolean cancel() { + if (progress != null) { + progress.setDisplayName(Bundle.CSVWriter_progress_cancelling()); + } + return ExportCSVAction.CSVWriter.this.cancel(true); + } + }); + progress.start(); + progress.switchToIndeterminate(); + + try (BufferedWriter br = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8))) { + // Write BOM + br.write('\ufeff'); + + // Write the header + List headers = new ArrayList<>(); + PropertySet[] sets = nodesToExport.get(0).getPropertySets(); + for(PropertySet set : sets) { + for (Property prop : set.getProperties()) { + headers.add(prop.getDisplayName()); + } + } + br.write(listToCSV(headers)); + + // Write each line + for (Node node : nodesToExport) { + if (this.isCancelled()) { + break; + } + + List values = new ArrayList<>(); + sets = node.getPropertySets(); + for(PropertySet set : sets) { + for (Property prop : set.getProperties()) { + values.add(prop.getValue().toString()); + } + } + br.write(listToCSV(values)); + } + } + + return null; + } + + private String listToCSV(List values) { + return "\"" + String.join("\",\"", values) + "\"\n"; + } + + @NbBundle.Messages({"CSVWriter.done.notifyMsg.error=Error exporting to CSV file", + "# {0} - Output file", + "CSVWriter.done.notifyMsg.success=Wrote to {0}"}) + @Override + protected void done() { + boolean msgDisplayed = false; + try { + super.get(); + } catch (InterruptedException | ExecutionException ex) { + logger.log(Level.SEVERE, "Fatal error during file extraction", ex); //NON-NLS + MessageNotifyUtil.Message.info(Bundle.CSVWriter_done_notifyMsg_error()); + msgDisplayed = true; + } catch (java.util.concurrent.CancellationException ex) { + // catch and ignore if we were cancelled + } finally { + progress.finish(); + if (!this.isCancelled() && !msgDisplayed) { + MessageNotifyUtil.Message.info(Bundle.CSVWriter_done_notifyMsg_success(outputFile)); + } + } + } + } +} diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AdHocSearchFilterNode.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AdHocSearchFilterNode.java index 57b84b11d6..cff13cc16e 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AdHocSearchFilterNode.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AdHocSearchFilterNode.java @@ -32,6 +32,7 @@ import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.openide.util.lookup.ProxyLookup; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; +import org.sleuthkit.autopsy.directorytree.ExportCSVAction; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.actions.AddContentTagAction; @@ -163,6 +164,7 @@ class AdHocSearchFilterNode extends FilterNode { actionsList.add(new ExternalViewerAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.openExternViewActLbl"), getOriginal())); actionsList.add(null); actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); From 20334eb7d5b81f3e41d54d89ee0d767a7cb0511b Mon Sep 17 00:00:00 2001 From: esaunders Date: Tue, 4 Jun 2019 13:47:41 -0400 Subject: [PATCH 035/106] Use asynchronous node creation for directory tree to support view file in directory functionality. --- .../corecomponents/DataResultViewerTable.java | 30 ++++++++++--------- .../datamodel/AbstractContentNode.java | 2 +- .../autopsy/datamodel/DataSourcesNode.java | 2 +- .../datamodel/RootContentChildren.java | 4 ++- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index a5bbc461b8..47662e3c8f 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -293,20 +293,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); try { - /* - * If the given node is not null and has children, set it as the - * root context of the child OutlineView, otherwise make an - * "empty"node the root context. - * - * IMPORTANT NOTE: This is the first of many times where a - * getChildren call on the current root node causes all of the - * children of the root node to be created and defeats lazy child - * node creation, if it is enabled. It also likely leads to many - * case database round trips. - */ - if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) { - this.rootNode = rootNode; - + if (rootNode != null) { /** * Check to see if we have previously created a paging support * class for this node. @@ -355,6 +342,21 @@ public class DataResultViewerTable extends AbstractDataResultViewer { // No-op } }); + } + + /* + * If the given node is not null and has children, set it as the + * root context of the child OutlineView, otherwise make an + * "empty"node the root context. + * + * IMPORTANT NOTE: This is the first of many times where a + * getChildren call on the current root node causes all of the + * children of the root node to be created and defeats lazy child + * node creation, if it is enabled. It also likely leads to many + * case database round trips. + */ + if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) { + this.rootNode = rootNode; this.getExplorerManager().setRootContext(this.rootNode); setupTable(); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index 4f6f2e5f47..29522bce4b 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -66,7 +66,7 @@ public abstract class AbstractContentNode extends ContentNode * @param lookup The Lookup object for the node. */ AbstractContentNode(T content, Lookup lookup) { - super(Children.create(new ContentChildren(content), true), lookup); + super(Children.create(new ContentChildren(content), false), 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/DataSourcesNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java index 1265db5658..5ab29a5376 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java @@ -57,7 +57,7 @@ public class DataSourcesNode extends DisplayableItemNode { } public DataSourcesNode(long dsObjId) { - super(Children.create(new DataSourcesNodeChildren(dsObjId), true), Lookups.singleton(NAME)); + super(Children.create(new DataSourcesNodeChildren(dsObjId), false), Lookups.singleton(NAME)); displayName = (dsObjId > 0) ? NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.group_by_datasource.name") : NAME; init(); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java index 66291955a8..c5d77692c8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java @@ -25,6 +25,7 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.datamodel.accounts.Accounts; +import org.sleuthkit.datamodel.SleuthkitVisitableItem; /** * Children implementation for the root node of a ContentNode tree. Accepts a @@ -34,6 +35,7 @@ public class RootContentChildren extends Children.Keys { private final Collection contentKeys; private final CreateAutopsyNodeVisitor createAutopsyNodeVisitor = new CreateAutopsyNodeVisitor(); + private final CreateSleuthkitNodeVisitor createSleuthkitNodeVisitor = new CreateSleuthkitNodeVisitor(); /** * @param contentKeys root Content objects for the Node tree @@ -68,7 +70,7 @@ public class RootContentChildren extends Children.Keys { if (key instanceof AutopsyVisitableItem) { return new Node[] {((AutopsyVisitableItem)key).accept(createAutopsyNodeVisitor)}; } else { - return null; + return new Node[] {((SleuthkitVisitableItem)key).accept(createSleuthkitNodeVisitor)}; } } From 4a5fbe3aeb7b3ad27b8f3c063f8f0dbc5a91f2ec Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 4 Jun 2019 14:35:17 -0400 Subject: [PATCH 036/106] 5092 working updates for ingest status --- .../datasourcesummary/DataSourceBrowser.java | 13 +++------- .../datasourcesummary/DataSourceSummary.java | 26 +++++-------------- .../DataSourceSummaryDialog.java | 17 ++++++++---- .../DataSourceSummaryNode.java | 2 +- 4 files changed, 23 insertions(+), 35 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java index 9c73ddadda..3fcb50b02e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java @@ -190,15 +190,12 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana return null; } - void refresh(long jobId, IngestJobInfo.IngestJobStatusType newStatus) { + void refresh(content datasource, IngestJobInfo.IngestJobStatusType newStatus) { Node[] selectedNodes = explorerManager.getSelectedNodes(); //attempt to update the status of any datasources that had status which was STARTED for (DataSourceSummary summary : dataSourceSummaryList) { - //attempt to update the status of any datasources that had status which was STARTED - //the database may not have been updated when this event is received so we need to manually update the UI - if (summary.getIngestStatus() == IngestJobInfo.IngestJobStatusType.STARTED && summary.getJobId() == jobId) { - System.out.println("UPDATING STATUS"); - summary.setStatus(newStatus); + if (summary.getDataSource().equals(datasource)) { + summary.updateStatusFromDatabase(); } } SwingUtilities.invokeLater(() -> { @@ -209,7 +206,6 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana //there should only be one selected node as multi-select is disabled for (Node selectedNode : selectedNodes) { if (((DataSourceSummaryEntryNode) node).getDataSource().equals(((DataSourceSummaryEntryNode) selectedNode).getDataSource())) { - System.out.println("NODE TO SELECT ADDED"); nodesToSelect.add(node); } } @@ -221,10 +217,9 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana } catch (PropertyVetoException ex) { logger.log(Level.WARNING, "Error selecting previously selected nodes", ex); } - revalidate(); - repaint(); }); + } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java index a3de258fe8..071be6e3cb 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java @@ -35,7 +35,6 @@ class DataSourceSummary { private final DataSource dataSource; private IngestJobStatusType status = null; - private Long jobId = null; private final String type; private final long filesCount; private final long resultsCount; @@ -54,29 +53,24 @@ class DataSourceSummary { */ DataSourceSummary(DataSource dSource, String typeValue, Long numberOfFiles, Long numberOfResults, Long numberOfTags) { dataSource = dSource; - getStatusFromDatabase(); + updateStatusFromDatabase(); type = typeValue == null ? "" : typeValue; filesCount = numberOfFiles == null ? 0 : numberOfFiles; resultsCount = numberOfResults == null ? 0 : numberOfResults; tagsCount = numberOfTags == null ? 0 : numberOfTags; } - private void getStatusFromDatabase() { + final void updateStatusFromDatabase() { try { IngestJobQueryCallback callback = new IngestJobQueryCallback(); - Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select("ingest_job_id, status_id FROM ingest_jobs WHERE obj_id=" + dataSource.getId(), callback); + Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select("status_id FROM ingest_jobs WHERE obj_id=" + dataSource.getId(), callback); status = callback.getStatus(); - jobId = callback.getJobId(); - System.out.println("NEW STATUS: " + status); + System.out.println("STATUS IN DB: " + status.getDisplayName()); } catch (NoCurrentCaseException | TskCoreException ex) { } } - - void setStatus(IngestJobStatusType newStatus){ - status = newStatus; - } - + /** * Get the DataSource * @@ -85,11 +79,7 @@ class DataSourceSummary { DataSource getDataSource() { return dataSource; } - - Long getJobId() { - return jobId; - } - + /** * Get the type of this DataSource * @@ -143,7 +133,6 @@ class DataSourceSummary { if (currentStatus == IngestJobStatusType.COMPLETED) { jobStatus = currentStatus; } else if (currentStatus == IngestJobStatusType.STARTED) { - ingestJobId = rs.getLong("ingest_job_id"); jobStatus = currentStatus; return; } @@ -157,8 +146,5 @@ class DataSourceSummary { return jobStatus; } - Long getJobId() { - return ingestJobId; - } } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java index 91ad7da792..1ac367c990 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java @@ -28,6 +28,8 @@ import javax.swing.event.ListSelectionEvent; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent; +import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent.Reason; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.IngestJobInfo; @@ -77,11 +79,16 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser } }); IngestManager.getInstance().addIngestJobEventListener((PropertyChangeEvent evt) -> { - if (evt.getPropertyName().equals(IngestManager.IngestJobEvent.CANCELLED.toString())){ - dataSourcesPanel.refresh((long)evt.getOldValue(), null); - } - else if (evt.getPropertyName().equals(IngestManager.IngestJobEvent.COMPLETED.toString())) { - dataSourcesPanel.refresh((long)evt.getOldValue(), IngestJobInfo.IngestJobStatusType.COMPLETED); + if (evt instanceof DataSourceAnalysisCompletedEvent) { + DataSourceAnalysisCompletedEvent dsEvent = (DataSourceAnalysisCompletedEvent) evt; + if (dsEvent.getResult() == Reason.ANALYSIS_COMPLETED) { + System.out.println("DS JOB ID: " + dsEvent.getDataSourceIngestJobId()); + System.out.println("JOB ID: " + dsEvent.getIngestJobId()); + dataSourcesPanel.refresh(dsEvent.getDataSource().getId(), IngestJobInfo.IngestJobStatusType.COMPLETED); + + } else if (dsEvent.getResult() == Reason.ANALYSIS_CANCELLED) { + dataSourcesPanel.refresh(dsEvent.getDataSource().getId(), null); + } } }); this.pack(); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java index 6c5a212d9d..b833e39743 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java @@ -126,7 +126,7 @@ final class DataSourceSummaryNode extends AbstractNode { DataSourceSummaryEntryNode(DataSourceSummary dataSourceSummary) { super(Children.LEAF); dataSource = dataSourceSummary.getDataSource(); - status = dataSourceSummary.getIngestStatus() == null ? "" : dataSourceSummary.getIngestStatus().toString(); + status = dataSourceSummary.getIngestStatus() == null ? "" : dataSourceSummary.getIngestStatus().getDisplayName(); type = dataSourceSummary.getType(); filesCount = dataSourceSummary.getFilesCount(); resultsCount = dataSourceSummary.getResultsCount(); From d2154cd3809de5981c86e6af32ce29a188327fea Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 4 Jun 2019 14:37:17 -0400 Subject: [PATCH 037/106] 5092 fix accidently reverted check of datasource id --- .../casemodule/datasourcesummary/DataSourceBrowser.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java index 3fcb50b02e..9f74f71e2e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java @@ -190,11 +190,11 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana return null; } - void refresh(content datasource, IngestJobInfo.IngestJobStatusType newStatus) { + void refresh(long dataSourceId, IngestJobInfo.IngestJobStatusType newStatus) { Node[] selectedNodes = explorerManager.getSelectedNodes(); //attempt to update the status of any datasources that had status which was STARTED for (DataSourceSummary summary : dataSourceSummaryList) { - if (summary.getDataSource().equals(datasource)) { + if (summary.getDataSource().getId() == dataSourceId) { summary.updateStatusFromDatabase(); } } From c1029fc154a5228395a5f9bbd4ec9191b0cbcf56 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 4 Jun 2019 14:41:20 -0400 Subject: [PATCH 038/106] 5092 reduce number of database queries necessary to display status --- .../casemodule/datasourcesummary/DataSourceBrowser.java | 2 +- .../casemodule/datasourcesummary/DataSourceSummary.java | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java index 9f74f71e2e..0df95720cf 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java @@ -195,7 +195,7 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana //attempt to update the status of any datasources that had status which was STARTED for (DataSourceSummary summary : dataSourceSummaryList) { if (summary.getDataSource().getId() == dataSourceId) { - summary.updateStatusFromDatabase(); + summary.setIngestStatus(newStatus); } } SwingUtilities.invokeLater(() -> { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java index 071be6e3cb..010c7b17ee 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java @@ -53,14 +53,14 @@ class DataSourceSummary { */ DataSourceSummary(DataSource dSource, String typeValue, Long numberOfFiles, Long numberOfResults, Long numberOfTags) { dataSource = dSource; - updateStatusFromDatabase(); + getStatusFromDatabase(); type = typeValue == null ? "" : typeValue; filesCount = numberOfFiles == null ? 0 : numberOfFiles; resultsCount = numberOfResults == null ? 0 : numberOfResults; tagsCount = numberOfTags == null ? 0 : numberOfTags; } - final void updateStatusFromDatabase() { + private void getStatusFromDatabase() { try { IngestJobQueryCallback callback = new IngestJobQueryCallback(); Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select("status_id FROM ingest_jobs WHERE obj_id=" + dataSource.getId(), callback); @@ -80,6 +80,10 @@ class DataSourceSummary { return dataSource; } + void setIngestStatus(IngestJobStatusType ingestStatus){ + status = ingestStatus; + } + /** * Get the type of this DataSource * From c9c451945db17955c1e4aaa80714fccb833a74c5 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 4 Jun 2019 15:02:28 -0400 Subject: [PATCH 039/106] 5092 add comments to better document how ingest job status is updated --- .../datasourcesummary/DataSourceBrowser.java | 11 +++-- .../datasourcesummary/DataSourceSummary.java | 47 +++++++++++++++---- .../DataSourceSummaryDialog.java | 5 -- 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java index 0df95720cf..f423b8a75a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java @@ -39,8 +39,6 @@ import org.sleuthkit.autopsy.casemodule.datasourcesummary.DataSourceSummaryNode. import static javax.swing.SwingConstants.RIGHT; import javax.swing.SwingUtilities; import javax.swing.table.TableColumn; -import org.openide.util.Exceptions; -import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.IngestJobInfo; import org.sleuthkit.datamodel.SleuthkitCase; @@ -61,7 +59,7 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana private final Outline outline; private final org.openide.explorer.view.OutlineView outlineView; private final ExplorerManager explorerManager; - private List dataSourceSummaryList; + private final List dataSourceSummaryList; private final RightAlignedTableCellRenderer rightAlignedRenderer = new RightAlignedTableCellRenderer(); /** @@ -190,6 +188,13 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana return null; } + /** + * Update the DataSourceBrowser to display up to date status information for + * the data sources. + * + * @param dataSourceId the ID of the data source which should be updated + * @param newStatus the new status which the data source should have + */ void refresh(long dataSourceId, IngestJobInfo.IngestJobStatusType newStatus) { Node[] selectedNodes = explorerManager.getSelectedNodes(); //attempt to update the status of any datasources that had status which was STARTED diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java index 010c7b17ee..fedb11884a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java @@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.logging.Level; +import java.util.logging.Logger; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.CaseDbAccessManager; @@ -33,6 +35,8 @@ import org.sleuthkit.datamodel.TskCoreException; */ class DataSourceSummary { + private static final Logger logger = Logger.getLogger(DataSourceSummary.class.getName()); + private static final String INGEST_JOB_STATUS_QUERY = "status_id FROM ingest_jobs WHERE obj_id="; private final DataSource dataSource; private IngestJobStatusType status = null; private final String type; @@ -60,17 +64,19 @@ class DataSourceSummary { tagsCount = numberOfTags == null ? 0 : numberOfTags; } + /** + * Get the status of the ingest job from the case database + */ private void getStatusFromDatabase() { try { IngestJobQueryCallback callback = new IngestJobQueryCallback(); - Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select("status_id FROM ingest_jobs WHERE obj_id=" + dataSource.getId(), callback); + Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select(INGEST_JOB_STATUS_QUERY + dataSource.getId(), callback); status = callback.getStatus(); - System.out.println("STATUS IN DB: " + status.getDisplayName()); } catch (NoCurrentCaseException | TskCoreException ex) { } } - + /** * Get the DataSource * @@ -79,11 +85,17 @@ class DataSourceSummary { DataSource getDataSource() { return dataSource; } - - void setIngestStatus(IngestJobStatusType ingestStatus){ + + /** + * Manually set the ingest job status + * + * @param ingestStatus the status which the ingest job should have + * currently, null to display empty string + */ + void setIngestStatus(IngestJobStatusType ingestStatus) { status = ingestStatus; } - + /** * Get the type of this DataSource * @@ -111,6 +123,12 @@ class DataSourceSummary { return resultsCount; } + /** + * Get the IngestJobStatusType associated with this data source. + * + * @return the IngestJobStatusType associated with this data source. Can be + * null if the IngestJobStatusType is not STARTED or COMPLETED. + */ IngestJobStatusType getIngestStatus() { return status; } @@ -124,28 +142,37 @@ class DataSourceSummary { return tagsCount; } + /** + * Callback to parse result set, getting the status to be associated with + * this data source + */ class IngestJobQueryCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { private IngestJobStatusType jobStatus = null; - private Long ingestJobId = null; @Override public void process(ResultSet rs) { try { while (rs.next()) { IngestJobStatusType currentStatus = IngestJobStatusType.fromID(rs.getInt("status_id")); - if (currentStatus == IngestJobStatusType.COMPLETED) { + if (currentStatus == IngestJobStatusType.COMPLETED) { jobStatus = currentStatus; - } else if (currentStatus == IngestJobStatusType.STARTED) { + } else if (currentStatus == IngestJobStatusType.STARTED) { jobStatus = currentStatus; return; } } } catch (SQLException ex) { - System.out.println("EEEP"); + logger.log(Level.WARNING, "Error getting status for ingest job", ex); } } + /** + * Get the status which was determined for this callback + * + * @return the status of the data source which was + * queried for + */ IngestJobStatusType getStatus() { return jobStatus; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java index 1ac367c990..f322731f42 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java @@ -23,7 +23,6 @@ import java.beans.PropertyChangeEvent; import java.util.Map; import java.util.Observable; import java.util.Observer; -import java.util.logging.Logger; import javax.swing.event.ListSelectionEvent; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; @@ -43,7 +42,6 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser private final DataSourceSummaryDetailsPanel detailsPanel; private final DataSourceBrowser dataSourcesPanel; private final IngestJobInfoPanel ingestHistoryPanel; - private static final Logger logger = Logger.getLogger(DataSourceSummaryDialog.class.getName()); /** * Creates new form DataSourceSummaryDialog for displaying a summary of the @@ -82,10 +80,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser if (evt instanceof DataSourceAnalysisCompletedEvent) { DataSourceAnalysisCompletedEvent dsEvent = (DataSourceAnalysisCompletedEvent) evt; if (dsEvent.getResult() == Reason.ANALYSIS_COMPLETED) { - System.out.println("DS JOB ID: " + dsEvent.getDataSourceIngestJobId()); - System.out.println("JOB ID: " + dsEvent.getIngestJobId()); dataSourcesPanel.refresh(dsEvent.getDataSource().getId(), IngestJobInfo.IngestJobStatusType.COMPLETED); - } else if (dsEvent.getResult() == Reason.ANALYSIS_CANCELLED) { dataSourcesPanel.refresh(dsEvent.getDataSource().getId(), null); } From 8637f1dfe0305ffb1e61609787d1d2cf140a55d4 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 4 Jun 2019 15:20:07 -0400 Subject: [PATCH 040/106] 5092 minor cleanup before pr --- .../casemodule/datasourcesummary/DataSourceBrowser.java | 4 +++- .../casemodule/datasourcesummary/DataSourceSummary.java | 3 +-- .../casemodule/datasourcesummary/DataSourceSummaryDialog.java | 1 + .../casemodule/datasourcesummary/DataSourceSummaryNode.java | 3 +-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java index f423b8a75a..ac09010de1 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java @@ -196,13 +196,15 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana * @param newStatus the new status which the data source should have */ void refresh(long dataSourceId, IngestJobInfo.IngestJobStatusType newStatus) { - Node[] selectedNodes = explorerManager.getSelectedNodes(); + //attempt to update the status of any datasources that had status which was STARTED for (DataSourceSummary summary : dataSourceSummaryList) { if (summary.getDataSource().getId() == dataSourceId) { summary.setIngestStatus(newStatus); } } + //figure out which nodes were previously selected + Node[] selectedNodes = explorerManager.getSelectedNodes(); SwingUtilities.invokeLater(() -> { explorerManager.setRootContext(new DataSourceSummaryNode(dataSourceSummaryList)); List nodesToSelect = new ArrayList<>(); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java index fedb11884a..c9c5b551ce 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java @@ -170,8 +170,7 @@ class DataSourceSummary { /** * Get the status which was determined for this callback * - * @return the status of the data source which was - * queried for + * @return the status of the data source which was queried for */ IngestJobStatusType getStatus() { return jobStatus; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java index f322731f42..1535cbefe0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java @@ -76,6 +76,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser this.repaint(); } }); + //add listener to refresh jobs with Started status when they complete IngestManager.getInstance().addIngestJobEventListener((PropertyChangeEvent evt) -> { if (evt instanceof DataSourceAnalysisCompletedEvent) { DataSourceAnalysisCompletedEvent dsEvent = (DataSourceAnalysisCompletedEvent) evt; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java index b833e39743..703e1f1373 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java @@ -184,8 +184,7 @@ final class DataSourceSummaryNode extends AbstractNode { } @Override - public Action[] getActions(boolean context - ) { + public Action[] getActions(boolean context) { List actions = new ArrayList<>(); actions.add(new ViewDataSourceInContextAction()); return actions.toArray(new Action[actions.size()]); From d34610fb58c118a75196a440d92cb5f43e74be7a Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 4 Jun 2019 15:32:21 -0400 Subject: [PATCH 041/106] 5092 address codacy complaints --- .../datasourcesummary/DataSourceSummary.java | 10 +++++----- .../datasourcesummary/DataSourceSummaryNode.java | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java index c9c5b551ce..390dce1afe 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java @@ -34,7 +34,7 @@ import org.sleuthkit.datamodel.TskCoreException; * */ class DataSourceSummary { - + private static final Logger logger = Logger.getLogger(DataSourceSummary.class.getName()); private static final String INGEST_JOB_STATUS_QUERY = "status_id FROM ingest_jobs WHERE obj_id="; private final DataSource dataSource; @@ -73,7 +73,7 @@ class DataSourceSummary { Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select(INGEST_JOB_STATUS_QUERY + dataSource.getId(), callback); status = callback.getStatus(); } catch (NoCurrentCaseException | TskCoreException ex) { - + logger.log(Level.WARNING, "Error getting status for data source from case database", ex); } } @@ -147,9 +147,9 @@ class DataSourceSummary { * this data source */ class IngestJobQueryCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { - + private IngestJobStatusType jobStatus = null; - + @Override public void process(ResultSet rs) { try { @@ -175,6 +175,6 @@ class DataSourceSummary { IngestJobStatusType getStatus() { return jobStatus; } - + } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java index 703e1f1373..0411d94bb2 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java @@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.Observable; import java.util.Observer; import javax.swing.Action; From 21615fca2f339b57b744f772d49b8588fe46ad35 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Wed, 5 Jun 2019 10:59:45 -0400 Subject: [PATCH 042/106] 5061 remove translation size setting --- .../BingTranslatorSettingsPanel.form | 56 +++---------------- .../BingTranslatorSettingsPanel.java | 42 +++----------- .../translators/Bundle.properties | 2 - .../GoogleTranslatorSettingsPanel.form | 4 ++ .../GoogleTranslatorSettingsPanel.java | 22 ++++++-- 5 files changed, 38 insertions(+), 88 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form index 64227561b3..54dfaa4827 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form @@ -39,28 +39,15 @@ - - - - + - - - - - - - - - - - - - - - - - + + + + + + + @@ -83,12 +70,6 @@ - - - - - - @@ -151,27 +132,6 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java index e477fbf662..5a73bca0fa 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java @@ -122,9 +122,6 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { testButton = new javax.swing.JButton(); targetLanguageLabel = new javax.swing.JLabel(); targetLanguageComboBox = new javax.swing.JComboBox<>(); - translationSizeLabel = new javax.swing.JLabel(); - translationSizeSpinner = new javax.swing.JSpinner(); - unitsLabel = new javax.swing.JLabel(); testUntranslatedTextField = new javax.swing.JTextField(); untranslatedLabel = new javax.swing.JLabel(); resultLabel = new javax.swing.JLabel(); @@ -151,12 +148,6 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { } }); - org.openide.awt.Mnemonics.setLocalizedText(translationSizeLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.translationSizeLabel.text")); // NOI18N - - translationSizeSpinner.setModel(new javax.swing.SpinnerNumberModel(5000, 5000, 500000, 5000)); - - org.openide.awt.Mnemonics.setLocalizedText(unitsLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.unitsLabel.text")); // NOI18N - testUntranslatedTextField.setText(DEFUALT_TEST_STRING); org.openide.awt.Mnemonics.setLocalizedText(untranslatedLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.untranslatedLabel.text")); // NOI18N @@ -190,24 +181,15 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { .addComponent(authenticationKeyField, javax.swing.GroupLayout.PREFERRED_SIZE, 486, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 20, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(translationSizeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(testButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(testButton, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(25, 25, 25) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(translationSizeSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(unitsLabel) - .addGap(0, 0, Short.MAX_VALUE)) - .addGroup(layout.createSequentialGroup() - .addComponent(untranslatedLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(resultLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(testResultValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))) + .addComponent(untranslatedLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(resultLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(testResultValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) .addContainerGap()))) ); layout.setVerticalGroup( @@ -222,11 +204,6 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { .addComponent(targetLanguageLabel) .addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(translationSizeLabel) - .addComponent(translationSizeSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(unitsLabel)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(testButton) .addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -264,9 +241,6 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { private javax.swing.JButton testButton; private javax.swing.JLabel testResultValueLabel; private javax.swing.JTextField testUntranslatedTextField; - private javax.swing.JLabel translationSizeLabel; - private javax.swing.JSpinner translationSizeSpinner; - private javax.swing.JLabel unitsLabel; private javax.swing.JLabel untranslatedLabel; private javax.swing.JLabel warningLabel; // End of variables declaration//GEN-END:variables diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties index 6ebd3002ef..0021118e14 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties @@ -6,9 +6,7 @@ BingTranslatorSettingsPanel.testButton.text=Test BingTranslatorSettingsPanel.testResultValueLabel.text= BingTranslatorSettingsPanel.resultLabel.text=Result: BingTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: -BingTranslatorSettingsPanel.translationSizeLabel.text=Translation Size: BingTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: -BingTranslatorSettingsPanel.unitsLabel.text=characters BingTranslatorSettingsPanel.authenticationKeyField.toolTipText=Enter the hash for the GoogleTranslatorSettingsPanel.testButton.text=Test GoogleTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form index 7d728fd011..b55a40b30f 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form @@ -145,6 +145,7 @@ + @@ -152,6 +153,7 @@ + @@ -159,6 +161,7 @@ + @@ -166,6 +169,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java index 8bccc01485..0041860182 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java @@ -130,21 +130,31 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { }); selectLanguageByCode(targetLanguageCode); targetLanguageComboBox.addItemListener(listener); - targetLanguageComboBox.setEnabled(true); + enableControls(true); + } else { - targetLanguageComboBox.setEnabled(false); + enableControls(false); } } else { warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_noFileSelected()); - targetLanguageComboBox.setEnabled(false); + enableControls(false); } } catch (Throwable throwable) { warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_unknownFailurePopulating()); logger.log(Level.WARNING, "Throwable caught while populating list of supported languages", throwable); - targetLanguageComboBox.setEnabled(false); + enableControls(false); } } + private void enableControls(boolean enabled) { + targetLanguageComboBox.setEnabled(enabled); + testButton.setEnabled(enabled); + testResultValueLabel.setEnabled(enabled); + testUntranslatedTextField.setEnabled(enabled); + untranslatedLabel.setEnabled(enabled); + resultLabel.setEnabled(enabled); + } + /** * Given a language code select the corresponding language in the combo box * if it is present @@ -202,12 +212,16 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(testResultValueLabel, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.testResultValueLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(resultLabel, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.resultLabel.text")); // NOI18N + resultLabel.setEnabled(false); org.openide.awt.Mnemonics.setLocalizedText(untranslatedLabel, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.untranslatedLabel.text")); // NOI18N + untranslatedLabel.setEnabled(false); testUntranslatedTextField.setText(DEFUALT_TEST_STRING); + testUntranslatedTextField.setEnabled(false); org.openide.awt.Mnemonics.setLocalizedText(testButton, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.testButton.text")); // NOI18N + testButton.setEnabled(false); testButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { testButtonActionPerformed(evt); From 2ecd3a56b4f5740dc03992b21a05765cd5dbcde7 Mon Sep 17 00:00:00 2001 From: Raman Date: Wed, 5 Jun 2019 11:13:32 -0400 Subject: [PATCH 043/106] Addressed review comments. --- .../relationships/MessageNode.java | 5 -- .../datamodel/AbstractAbstractFileNode.java | 10 +-- .../datamodel/AbstractContentNode.java | 7 +- .../datamodel/BlackboardArtifactNode.java | 82 +++++++++++++++---- .../datamodel/Bundle.properties-MERGED | 2 +- .../autopsy/datamodel/GetSCOTask.java | 2 +- 6 files changed, 79 insertions(+), 29 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java index 2f4ce1157a..d1cdcbabf2 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java @@ -18,13 +18,10 @@ */ package org.sleuthkit.autopsy.communications.relationships; -import java.util.List; import java.util.TimeZone; import java.util.logging.Level; import org.apache.commons.lang3.StringUtils; import org.openide.nodes.Sheet; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; -import org.sleuthkit.autopsy.core.UserPreferences; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; @@ -40,7 +37,6 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHO import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT; import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME; -import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.communications.Utils; @@ -72,7 +68,6 @@ final class MessageNode extends BlackboardArtifactNode { @Override protected Sheet createSheet() { Sheet sheet = super.createSheet(); - List tags = getAllTagsFromDatabase(); Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); if (sheetSet == null) { sheetSet = Sheet.createPropertiesSet(); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index e8d71caae7..158f2ef4d9 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -74,8 +74,6 @@ import org.sleuthkit.datamodel.TskData; public abstract class AbstractAbstractFileNode extends AbstractContentNode { private static final Logger logger = Logger.getLogger(AbstractAbstractFileNode.class.getName()); - @NbBundle.Messages("AbstractAbstractFileNode.addFileProperty.desc=no description") - private static final String NO_DESCR = AbstractAbstractFileNode_addFileProperty_desc(); private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE, Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, Case.Events.CR_COMMENT_CHANGED); @@ -328,9 +326,11 @@ public abstract class AbstractAbstractFileNode extends A } // Create place holders for S C O - properties.add(new NodeProperty<>(SCORE.toString(), SCORE.toString(), NO_DESCR, "")); - properties.add(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, "")); - properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), NO_DESCR, "")); + properties.add(new NodeProperty<>(SCORE.toString(), SCORE.toString(), VALUE_LOADING, "")); + properties.add(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), VALUE_LOADING, "")); + if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) { + properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), VALUE_LOADING, "")); + } // Get the SCO columns data in a background task diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index 154ca68d50..b9e012e6af 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -64,14 +64,15 @@ public abstract class AbstractContentNode extends ContentNode * populate this node. */ static final ExecutorService backgroundTasksPool; - static final Integer MAX_POOL_SIZE = 10; + private static final Integer MAX_POOL_SIZE = 10; /** * Default no description string */ - @NbBundle.Messages("AbstractContentNode.nodescription=no description") + @NbBundle.Messages({"AbstractContentNode.nodescription=no description", + "AbstractContentNode.valueLoading=value loading"}) protected static final String NO_DESCR = Bundle.AbstractContentNode_nodescription(); - + protected static final String VALUE_LOADING = Bundle.AbstractContentNode_valueLoading(); /** * Event signals to indicate the background tasks have completed processing. diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 1f871363f9..23ab78fa01 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -99,10 +99,7 @@ public class BlackboardArtifactNode extends AbstractContentNode> customProperties; - - protected final static String NO_DESCR = NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.noDesc.text"); - - + /* * Artifact types which should have the full unique path of the associated * content as a property. @@ -367,9 +364,11 @@ public class BlackboardArtifactNode extends AbstractContentNode(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), NO_DESCR, "")); - sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, "")); - sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), VALUE_LOADING, "")); + sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), VALUE_LOADING, "")); + if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) { + sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), VALUE_LOADING, "")); + } // Get the SCO columns data in a background task backgroundTasksPool.submit(new GetSCOTask( @@ -596,14 +595,33 @@ public class BlackboardArtifactNode extends AbstractContentNode tags, CorrelationAttributeInstance attribute) { + HasCommentStatus status = getCommentProperty(tags, attribute ); + sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, + status)); + } + + /** + * Gets the comment property for the node + * * @param tags the list of tags associated with the file * @param attribute the correlation attribute associated with this * artifact's associated file, null if central repo is not * enabled * @return comment property */ - @NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C", - "BlackboardArtifactNode.createSheet.comment.displayName=C"}) @Override protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { @@ -627,14 +645,15 @@ public class BlackboardArtifactNode extends AbstractContentNode tags) { + Pair scoreAndDescription = getScorePropertyAndDescription(tags); + sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft())); + } + /** + * Get the score property for the node. + * + * @param tags the list of tags associated with the file + * + * @return score property and description + */ @Override protected Pair getScorePropertyAndDescription(List tags) { Score score = Score.NO_SCORE; @@ -693,17 +724,40 @@ public class BlackboardArtifactNode extends AbstractContentNode countAndDescription = getCountPropertyAndDescription(attribute); + sheetSet.put( + new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), countAndDescription.getRight(), countAndDescription.getLeft())); + } + + /** + * Gets the Occurrences property for the node. + * + * @param attribute correlation attribute instance + * + * @return count and description + * + */ @Override protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) { - Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting + Long count = -1L; String description = Bundle.BlackboardArtifactNode_createSheet_count_noCentralRepo_description(); try { //don't perform the query if there is no correlation value diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED index 8d2bd5952d..0386b0fef2 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED @@ -1,5 +1,4 @@ AbstractAbstractFileNode.accessTimeColLbl=Access Time -AbstractAbstractFileNode.addFileProperty.desc=no description AbstractAbstractFileNode.attrAddrColLbl=Attr. Addr. AbstractAbstractFileNode.changeTimeColLbl=Change Time AbstractAbstractFileNode.createdTimeColLbl=Created Time @@ -38,6 +37,7 @@ AbstractAbstractFileNode.typeDirColLbl=Type(Dir) AbstractAbstractFileNode.typeMetaColLbl=Type(Meta) AbstractAbstractFileNode.useridColLbl=UserID AbstractContentNode.nodescription=no description +AbstractContentNode.valueLoading=value loading AbstractFsContentNode.noDesc.text=no description ArtifactStringContent.attrsTableHeader.sources=Source(s) ArtifactStringContent.attrsTableHeader.type=Type diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java index 4278f99d66..b7f4e38a5e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java @@ -37,7 +37,7 @@ class GetSCOTask implements Runnable { private final WeakReference> weakNodeRef; private final PropertyChangeListener listener; - public GetSCOTask(WeakReference> weakContentRef, PropertyChangeListener listener) { + GetSCOTask(WeakReference> weakContentRef, PropertyChangeListener listener) { this.weakNodeRef = weakContentRef; this.listener = listener; } From 852ad67e05f59a3e2860210136e464de054006e7 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Wed, 5 Jun 2019 12:16:16 -0400 Subject: [PATCH 044/106] 5061 fix initial selection of english --- .../BingTranslatorSettingsPanel.form | 1 + .../BingTranslatorSettingsPanel.java | 15 ++++++--- .../translators/Bundle.properties-MERGED | 5 +-- .../translators/GoogleTranslator.java | 31 +++++++++++-------- .../GoogleTranslatorSettingsPanel.java | 24 +++++++------- 5 files changed, 46 insertions(+), 30 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form index 54dfaa4827..cd855ac419 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form @@ -124,6 +124,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java index 5a73bca0fa..c6d4c9b932 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java @@ -33,6 +33,7 @@ import java.util.logging.Logger; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import org.apache.commons.lang3.StringUtils; +import org.openide.util.NbBundle.Messages; /** * Settings panel for the GoogleTranslator @@ -68,11 +69,12 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { } }); - targetLanguageCode = code; populateComboBox(); - selectLanguageByCode(targetLanguageCode); + selectLanguageByCode(code); + targetLanguageCode = code; } + @Messages({"BingTranslatorSettingsPanel.warning.targetLanguageFailure=Unable to get list of target languages or parse the result that was received"}) private void populateComboBox() { Request get_request = new Request.Builder() .url(GET_TARGET_LANGUAGES_URL).build(); @@ -87,8 +89,11 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { responses.entrySet().forEach((entry) -> { targetLanguageComboBox.addItem(new LanguageWrapper(entry.getKey(), entry.getValue().getAsJsonObject().get("name").getAsString())); }); + targetLanguageComboBox.setEnabled(true); } catch (IOException | IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException ex) { - logger.log(Level.WARNING, "Unable to get list of target languages or parse the result that was received", ex); + logger.log(Level.SEVERE, Bundle.BingTranslatorSettingsPanel_warning_targetLanguageFailure(), ex); + warningLabel.setText(Bundle.BingTranslatorSettingsPanel_warning_targetLanguageFailure()); + targetLanguageComboBox.setEnabled(false); } } @@ -142,6 +147,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(targetLanguageLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.targetLanguageLabel.text")); // NOI18N + targetLanguageComboBox.setEnabled(false); targetLanguageComboBox.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { targetLanguageComboBoxSelected(evt); @@ -216,11 +222,12 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { ); }// //GEN-END:initComponents + @Messages({"BingTranslatorSettingsPanel.warning.invalidKey=Invalid translation authentication key"}) private void testButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_testButtonActionPerformed if (testTranslationSetup()) { warningLabel.setText(""); } else { - warningLabel.setText("Invalid translation authentication key"); + warningLabel.setText(Bundle.BingTranslatorSettingsPanel_warning_invalidKey()); } }//GEN-LAST:event_testButtonActionPerformed diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED index 35e06bda99..51cbb9a26f 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED @@ -1,9 +1,12 @@ BingTranslator.name.text=Bing Translator +BingTranslatorSettingsPanel.warning.invalidKey=Invalid translation authentication key +BingTranslatorSettingsPanel.warning.targetLanguageFailure=Unable to get list of target languages or parse the result that was received GoogleTranslator.name.text=Google Translate GoogleTranslatorSettingsPanel.browseButton.text=Browse GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials Path: GoogleTranslatorSettingsPanel.errorMessage.fileNotFound=Credentials file not found, please set the location to be a valid JSON credentials file. GoogleTranslatorSettingsPanel.errorMessage.noFileSelected=A JSON file must be selected to provide your credentials for Google Translate. +GoogleTranslatorSettingsPanel.errorMessage.translationFailure=Translation failure with specified credentials GoogleTranslatorSettingsPanel.errorMessage.unableToMakeCredentials=Unable to construct credentials object from credentials file, please set the location to be a valid JSON credentials file. GoogleTranslatorSettingsPanel.errorMessage.unableToReadCredentials=Unable to read credentials from credentials file, please set the location to be a valid JSON credentials file. GoogleTranslatorSettingsPanel.errorMessage.unknownFailureGetting=Failure getting list of supported languages with current credentials file. @@ -16,9 +19,7 @@ BingTranslatorSettingsPanel.testButton.text=Test BingTranslatorSettingsPanel.testResultValueLabel.text= BingTranslatorSettingsPanel.resultLabel.text=Result: BingTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: -BingTranslatorSettingsPanel.translationSizeLabel.text=Translation Size: BingTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: -BingTranslatorSettingsPanel.unitsLabel.text=characters BingTranslatorSettingsPanel.authenticationKeyField.toolTipText=Enter the hash for the GoogleTranslatorSettingsPanel.testButton.text=Test GoogleTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java index d55316b86c..3bb6a6c9ed 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java @@ -32,6 +32,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.logging.Level; import java.util.logging.Logger; +import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.coreutils.EscapeUtil; @@ -59,26 +60,26 @@ public final class GoogleTranslator implements TextTranslator { settingsPanel = new GoogleTranslatorSettingsPanel(settings.getCredentialPath(), settings.getTargetLanguageCode()); loadTranslator(); } - + private static boolean googleIsReachable() { String host = "www.google.com"; InetAddress address; try { address = InetAddress.getByName(host); return address.isReachable(1500); - }catch (UnknownHostException ex) { + } catch (UnknownHostException ex) { return false; } catch (IOException ex) { return false; } } - + @Override public String translate(String string) throws TranslationException { if (!googleIsReachable()) { throw new TranslationException("Failure translating using GoogleTranslator: Cannot connect to Google"); } - + if (googleTranslate != null) { try { // Translates some text into English, without specifying the source language. @@ -89,7 +90,7 @@ public final class GoogleTranslator implements TextTranslator { // We can't currently set parameters, so we are using the default behavior of // assuming the input is HTML. We need to replace newlines with
for Google to preserve them substring = substring.replaceAll("(\r\n|\n)", "
"); - + // The API complains if the "Payload" is over 204800 bytes. I'm assuming that // deals with the full request. At some point, we get different errors about too // much text. Officially, Google says they will googleTranslate only 5k chars, @@ -101,15 +102,15 @@ public final class GoogleTranslator implements TextTranslator { Translation translation = googleTranslate.translate(substring); String translatedString = translation.getTranslatedText(); - + // put back the newlines translatedString = translatedString.replaceAll("
", "\n"); - + // With our current settings, Google Translate outputs HTML // so we need to undo the escape characters. translatedString = EscapeUtil.unEscapeHtml(translatedString); return translatedString; - } catch (Throwable ex) { + } catch (Throwable ex) { //Catching throwables because some of this Google Translate code throws throwables throw new TranslationException("Failure translating using GoogleTranslator", ex); } @@ -117,7 +118,7 @@ public final class GoogleTranslator implements TextTranslator { throw new TranslationException("Google Translator has not been configured, credentials need to be specified"); } } - + @Messages({"GoogleTranslator.name.text=Google Translate"}) @Override public String getName() { @@ -136,10 +137,14 @@ public final class GoogleTranslator implements TextTranslator { private void loadTranslator() { InputStream credentialStream = null; Credentials creds = null; - try { - credentialStream = new FileInputStream(settings.getCredentialPath()); - } catch (FileNotFoundException ex) { - logger.log(Level.WARNING, "JSON file for GoogleTranslator credentials not found", ex); + if (StringUtils.isBlank(settings.getCredentialPath())) { + logger.log(Level.INFO, "No credentials file has been provided for Google Translator"); + } else { + try { + credentialStream = new FileInputStream(settings.getCredentialPath()); + } catch (FileNotFoundException ex) { + logger.log(Level.WARNING, "JSON file for GoogleTranslator credentials not found", ex); + } } if (credentialStream != null) { try { diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java index 0041860182..4032d69a05 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java @@ -305,18 +305,20 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { } }//GEN-LAST:event_browseButtonActionPerformed + @Messages({"GoogleTranslatorSettingsPanel.errorMessage.translationFailure=Translation failure with specified credentials"}) private void testButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_testButtonActionPerformed - testResultValueLabel.setText(""); - Translate tempTranslate = getTemporaryTranslationService(); - if (tempTranslate != null) { - try { - Translation translation = tempTranslate.translate(testUntranslatedTextField.getText()); - testResultValueLabel.setText(translation.getTranslatedText()); - warningLabel.setText(""); - } catch (Exception ex) { - warningLabel.setText("Invalid translation credentials path"); - } - } + testResultValueLabel.setText(""); + Translate tempTranslate = getTemporaryTranslationService(); + if (tempTranslate != null) { + try { + Translation translation = tempTranslate.translate(testUntranslatedTextField.getText()); + testResultValueLabel.setText(translation.getTranslatedText()); + warningLabel.setText(""); + } catch (Exception ex) { + warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_translationFailure()); + logger.log(Level.WARNING, Bundle.GoogleTranslatorSettingsPanel_errorMessage_translationFailure(), ex); + } + } }//GEN-LAST:event_testButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables From f8d02a3002e361f9d94be229b16bd75163c0d67b Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Wed, 5 Jun 2019 12:19:38 -0400 Subject: [PATCH 045/106] Added button --- .../autopsy/corecomponents/Bundle.properties | 1 + .../corecomponents/Bundle.properties-MERGED | 1 + .../corecomponents/DataResultViewerTable.form | 24 +++- .../corecomponents/DataResultViewerTable.java | 29 +++-- .../directorytree/Bundle.properties-MERGED | 6 +- .../directorytree/ExportCSVAction.java | 117 ++++++++++++------ 6 files changed, 122 insertions(+), 56 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index 7eb261880f..fae74a30ac 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -217,3 +217,4 @@ DataResultViewerTable.pageNumLabel.text= DataResultViewerTable.pageLabel.text=Page: ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table: ViewPreferencesPanel.maxResultsLabel.toolTipText=\nSetting this value to 0 will display all results in the results table.\n
Note that setting this value to 0 may result in poor UI responsiveness when there are large numbers of results.\n +DataResultViewerTable.exportCSVButton.text=Save table as CSV diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED index cd75ae919e..43ceba39ba 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED @@ -270,3 +270,4 @@ DataResultViewerTable.pageNumLabel.text= DataResultViewerTable.pageLabel.text=Page: ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table: ViewPreferencesPanel.maxResultsLabel.toolTipText=\nSetting this value to 0 will display all results in the results table.\n
Note that setting this value to 0 may result in poor UI responsiveness when there are large numbers of results.\n +DataResultViewerTable.exportCSVButton.text=Save table as CSV diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form index 6b17cfd6dd..87e771695b 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form @@ -16,9 +16,10 @@ - - - + + + + @@ -39,7 +40,7 @@ - + @@ -48,9 +49,10 @@ + - - + + @@ -164,5 +166,15 @@
+ + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index a5bbc461b8..eb5fad97f5 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -1291,6 +1291,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { outlineView = new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL); gotoPageLabel = new javax.swing.JLabel(); gotoPageTextField = new javax.swing.JTextField(); + exportCSVButton = new javax.swing.JButton(); pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageLabel.text")); // NOI18N @@ -1338,13 +1339,21 @@ public class DataResultViewerTable extends AbstractDataResultViewer { } }); + exportCSVButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.exportCSVButton.text")); // NOI18N + exportCSVButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + exportCSVButtonActionPerformed(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(608, Short.MAX_VALUE) + .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 904, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(exportCSVButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(pageLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -1366,7 +1375,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() + .addGap(3, 3, 3) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) .addComponent(pageLabel) .addComponent(pageNumLabel) @@ -1374,9 +1383,10 @@ public class DataResultViewerTable extends AbstractDataResultViewer { .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) + .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(exportCSVButton)) + .addGap(3, 3, 3) + .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 321, Short.MAX_VALUE) .addContainerGap()) ); @@ -1397,7 +1407,12 @@ public class DataResultViewerTable extends AbstractDataResultViewer { pagingSupport.gotoPage(); }//GEN-LAST:event_gotoPageTextFieldActionPerformed + private void exportCSVButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCSVButtonActionPerformed + org.sleuthkit.autopsy.directorytree.ExportCSVAction.saveNodesToCSV(java.util.Arrays.asList(rootNode.getChildren().getNodes()), this); + }//GEN-LAST:event_exportCSVButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton exportCSVButton; private javax.swing.JLabel gotoPageLabel; private javax.swing.JTextField gotoPageTextField; private org.openide.explorer.view.OutlineView outlineView; diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED index d58338c450..6ec6a0e76b 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED @@ -11,9 +11,9 @@ DirectoryTreeTopComponent.componentOpened.groupDataSources.title=Group by data s DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module. DirectoryTreeTopComponent.resultsView.title=Listing # {0} - Output file -ExportCSV.actionPerformed.fileExists=File {0} already exists -ExportCSV.actionPerformed.noCurrentCase=No open case available -ExportCSV.title.text=Export to CSV +ExportCSV.saveNodesToCSV.fileExists=File {0} already exists +ExportCSV.saveNodesToCSV.noCurrentCase=No open case available +ExportCSV.title.text=Export selected rows to CSV ExternalViewerAction.actionPerformed.failure.exe.message=The file is an executable and will not be opened. ExternalViewerAction.actionPerformed.failure.IO.message=There is no associated editor for files of this type or the associated application failed to launch. ExternalViewerAction.actionPerformed.failure.missingFile.message=The file no longer exists. diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java index 169701a55c..dce6244084 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java @@ -25,8 +25,10 @@ import java.io.BufferedWriter; import java.io.FileWriter; import java.io.FileOutputStream; import java.io.OutputStreamWriter; +import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.HashSet; @@ -63,9 +65,11 @@ import org.openide.nodes.Node.Property; */ public final class ExportCSVAction extends AbstractAction { - private Logger logger = Logger.getLogger(ExportCSVAction.class.getName()); + private static Logger logger = Logger.getLogger(ExportCSVAction.class.getName()); + private final static String DEFAULT_FILENAME = "Results"; + private final static List columnsToSkip = Arrays.asList("S", "C", "O"); - private String userDefinedExportPath; + private static volatile String userDefinedExportPath; // TODO make volatile or whatever // This class is a singleton to support multi-selection of nodes, since // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every @@ -82,53 +86,44 @@ public final class ExportCSVAction extends AbstractAction { /** * Private constructor for the action. */ - @NbBundle.Messages({"ExportCSV.title.text=Export to CSV"}) + @NbBundle.Messages({"ExportCSV.title.text=Export selected rows to CSV"}) private ExportCSVAction() { super(Bundle.ExportCSV_title_text()); } - + /** * Asks user to choose destination, then extracts content to destination * (recursing on directories). * * @param e The action event. */ - @NbBundle.Messages({ - "# {0} - Output file", - "ExportCSV.actionPerformed.fileExists=File {0} already exists", - "ExportCSV.actionPerformed.noCurrentCase=No open case available"}) + @Override public void actionPerformed(ActionEvent e) { - Collection selectedNodes = Utilities.actionsGlobalContext().lookupAll(Node.class); - if (selectedNodes.isEmpty()) { + saveNodesToCSV(selectedNodes, (Component)e.getSource()); + } + + @NbBundle.Messages({ + "# {0} - Output file", + "ExportCSV.saveNodesToCSV.fileExists=File {0} already exists", + "ExportCSV.saveNodesToCSV.noCurrentCase=No open case available"}) + public static void saveNodesToCSV(Collection nodesToExport, Component component) { + + if (nodesToExport.isEmpty()) { return; } - Node parent = selectedNodes.iterator().next().getParentNode(); - if (parent != null) { - System.out.println("HTML name: " + parent.getHtmlDisplayName()); - System.out.println("Display name: " + parent.getDisplayName()); - System.out.println("Class: " + parent.getClass().getCanonicalName()); - for (PropertySet set : parent.getPropertySets()) { - for (Property prop : set.getProperties()) { - try { - System.out.println(" " + prop.getDisplayName() + " : " + prop.getValue().toString()); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - } - } - try { - String fileName = String.format("%1$tY%1$tm%1$te%1$tI%1$tM%1$tS_listing.csv", Calendar.getInstance()); + + String fileName = getDefaultOutputFileName(nodesToExport.iterator().next().getParentNode()); + JFileChooser fileChooser = new JFileChooser(); fileChooser.setCurrentDirectory(new File(getExportDirectory(Case.getCurrentCaseThrows()))); fileChooser.setSelectedFile(new File(fileName)); fileChooser.setFileFilter(new FileNameExtensionFilter("csv file", "csv")); - int returnVal = fileChooser.showSaveDialog((Component) e.getSource()); + int returnVal = fileChooser.showSaveDialog(component); if (returnVal == JFileChooser.APPROVE_OPTION) { File selectedFile = fileChooser.getSelectedFile(); @@ -143,15 +138,51 @@ public final class ExportCSVAction extends AbstractAction { return; } - CSVWriter writer = new CSVWriter(selectedNodes, selectedFile); + CSVWriter writer = new CSVWriter(nodesToExport, selectedFile); writer.execute(); } } catch (NoCurrentCaseException ex) { - JOptionPane.showMessageDialog((Component) e.getSource(), Bundle.ExportCSV_actionPerformed_noCurrentCase()); + JOptionPane.showMessageDialog(component, Bundle.ExportCSV_actionPerformed_noCurrentCase()); logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS } } + /** + * Create a default name for the CSV output. + * + * @param parent The parent node for the selected nodes + * + * @return the default name + */ + private static String getDefaultOutputFileName(Node parent) { + String dateStr = String.format("%1$tY%1$tm%1$te%1$tI%1$tM%1$tS", Calendar.getInstance()); + + if (parent != null) { + // The first value in the property set is generally a reasonable name + for (PropertySet set : parent.getPropertySets()) { + for (Property prop : set.getProperties()) { + try { + String parentName = prop.getValue().toString(); + + // Strip off the count (if present) + System.out.println("parentName (raw) : " + parentName); + parentName = parentName.replaceAll("\\([0-9]+\\)$", ""); + System.out.println("parentName (after paren regex) : " + parentName); + + // Strip out any invalid characters + parentName = parentName.replaceAll("[\\\\/:*?\"<>|]", "_"); + System.out.println("parentName (after char regex) : " + parentName); + + return parentName + " " + dateStr; + } catch (IllegalAccessException | InvocationTargetException ex) { + logger.log(Level.WARNING, "Failed to get property set value as string", ex); + } + } + } + } + return DEFAULT_FILENAME + " " + dateStr; + } + /** * Get the export directory path. * @@ -159,7 +190,7 @@ public final class ExportCSVAction extends AbstractAction { * * @return The export directory path. */ - private String getExportDirectory(Case openCase) { + private static String getExportDirectory(Case openCase) { // TODO sync String caseExportPath = openCase.getExportDirectory(); if (userDefinedExportPath == null) { @@ -183,7 +214,7 @@ public final class ExportCSVAction extends AbstractAction { * @param exportPath The export path. * @param openCase The current case. */ - private void updateExportDirectory(String exportPath, Case openCase) { + private static void updateExportDirectory(String exportPath, Case openCase) { // TODO sync if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) { userDefinedExportPath = null; } else { @@ -195,12 +226,12 @@ public final class ExportCSVAction extends AbstractAction { /** * Thread that does the actual extraction work */ - private class CSVWriter extends SwingWorker { + private static class CSVWriter extends SwingWorker { private final Logger logger = Logger.getLogger(CSVWriter.class.getName()); private ProgressHandle progress; - private final List nodesToExport; + private final Collection nodesToExport; private final File outputFile; /** @@ -208,8 +239,8 @@ public final class ExportCSVAction extends AbstractAction { * * @param extractionTasks List of file extraction tasks. */ - CSVWriter(Collection selectedNodes, File outputFile) { - this.nodesToExport = new ArrayList<>(selectedNodes); + CSVWriter(Collection nodesToExport, File outputFile) { + this.nodesToExport = nodesToExport; this.outputFile = outputFile; } @@ -241,25 +272,31 @@ public final class ExportCSVAction extends AbstractAction { // Write the header List headers = new ArrayList<>(); - PropertySet[] sets = nodesToExport.get(0).getPropertySets(); + PropertySet[] sets = nodesToExport.iterator().next().getPropertySets(); for(PropertySet set : sets) { for (Property prop : set.getProperties()) { - headers.add(prop.getDisplayName()); + if ( ! columnsToSkip.contains(prop.getDisplayName())) { + headers.add(prop.getDisplayName()); + } } } br.write(listToCSV(headers)); // Write each line - for (Node node : nodesToExport) { + Iterator nodeIterator = nodesToExport.iterator(); + while (nodeIterator.hasNext()) { if (this.isCancelled()) { break; } + Node node = (Node)nodeIterator.next(); List values = new ArrayList<>(); sets = node.getPropertySets(); for(PropertySet set : sets) { for (Property prop : set.getProperties()) { - values.add(prop.getValue().toString()); + if ( ! columnsToSkip.contains(prop.getDisplayName())) { + values.add(prop.getValue().toString()); + } } } br.write(listToCSV(values)); From 3d319be5b8af8f35cb48868e27399c7ab662af37 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Wed, 5 Jun 2019 12:27:53 -0400 Subject: [PATCH 046/106] 5061 adjust comments for clarity and completeness --- .../translators/BingTranslator.java | 28 ++++++++++++++--- .../BingTranslatorSettingsPanel.java | 3 ++ .../translators/GoogleTranslator.java | 7 ++++- .../GoogleTranslatorSettingsPanel.java | 30 +++++++++++-------- 4 files changed, 51 insertions(+), 17 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java index 78f1cf9afd..775cdf3588 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java @@ -40,22 +40,32 @@ import org.sleuthkit.autopsy.texttranslation.TranslationException; @ServiceProvider(service = TextTranslator.class) public class BingTranslator implements TextTranslator { - //In the String below, "en" is the target language. You can include multiple target + //The target language follows the to= in the string below. You can include multiple target //languages separated by commas. A full list of supported languages is here: //https://docs.microsoft.com/en-us/azure/cognitive-services/translator/language-support private static final String BASE_URL = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to="; + private static final int MAX_STRING_LENGTH = 5000; private final BingTranslatorSettingsPanel settingsPanel; private final BingTranslatorSettings settings = new BingTranslatorSettings(); // This sends messages to Microsoft. private final OkHttpClient CLIENT = new OkHttpClient(); - //We might want to make this a configurable setting for anyone who has a - //paid account that's willing to pay for long documents. - private final int MAX_STRING_LENGTH = 5000; + /** + * Create a Bing Translator + */ public BingTranslator() { settingsPanel = new BingTranslatorSettingsPanel(settings.getAuthenticationKey(), settings.getTargetLanguageCode()); } + /** + * Get the tranlationurl for the specified language code + * + * + * + * @param languageCode language code for language to translate to + * + * @return a string representation of the url to request translation from + */ static String getTranlatorUrl(String languageCode) { return BASE_URL + languageCode; } @@ -134,6 +144,16 @@ public class BingTranslator implements TextTranslator { settings.saveSettings(); } + /** + * Parse the response to get the translated text + * + * @param json_text the json which was received as a response to a + * translation request + * + * @return the translated text + * + * @throws TranslationException + */ private String parseJSONResponse(String json_text) throws TranslationException { /* * Here is an example of the text we get from Bing when input is "gato", diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java index c6d4c9b932..cb115354e2 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java @@ -74,6 +74,9 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { targetLanguageCode = code; } + /** + * Populate the target language combo box with available target languages + */ @Messages({"BingTranslatorSettingsPanel.warning.targetLanguageFailure=Unable to get list of target languages or parse the result that was received"}) private void populateComboBox() { Request get_request = new Request.Builder() diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java index 3bb6a6c9ed..de61222072 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java @@ -61,6 +61,11 @@ public final class GoogleTranslator implements TextTranslator { loadTranslator(); } + /** + * Check if google is able to be reached + * + * @return true if it can be, false otherwise + */ private static boolean googleIsReachable() { String host = "www.google.com"; InetAddress address; @@ -138,7 +143,7 @@ public final class GoogleTranslator implements TextTranslator { InputStream credentialStream = null; Credentials creds = null; if (StringUtils.isBlank(settings.getCredentialPath())) { - logger.log(Level.INFO, "No credentials file has been provided for Google Translator"); + logger.log(Level.INFO, "No credentials file has been provided for Google Translator"); } else { try { credentialStream = new FileInputStream(settings.getCredentialPath()); diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java index 4032d69a05..84fad9e8e1 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java @@ -146,6 +146,12 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { } } + /** + * Helper method to enable/disable all controls which are dependent on valid + * credentials having been provided + * + * @param enabled true to enable the controls, false to disable them + */ private void enableControls(boolean enabled) { targetLanguageComboBox.setEnabled(enabled); testButton.setEnabled(enabled); @@ -307,18 +313,18 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { @Messages({"GoogleTranslatorSettingsPanel.errorMessage.translationFailure=Translation failure with specified credentials"}) private void testButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_testButtonActionPerformed - testResultValueLabel.setText(""); - Translate tempTranslate = getTemporaryTranslationService(); - if (tempTranslate != null) { - try { - Translation translation = tempTranslate.translate(testUntranslatedTextField.getText()); - testResultValueLabel.setText(translation.getTranslatedText()); - warningLabel.setText(""); - } catch (Exception ex) { - warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_translationFailure()); - logger.log(Level.WARNING, Bundle.GoogleTranslatorSettingsPanel_errorMessage_translationFailure(), ex); - } - } + testResultValueLabel.setText(""); + Translate tempTranslate = getTemporaryTranslationService(); + if (tempTranslate != null) { + try { + Translation translation = tempTranslate.translate(testUntranslatedTextField.getText()); + testResultValueLabel.setText(translation.getTranslatedText()); + warningLabel.setText(""); + } catch (Exception ex) { + warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_translationFailure()); + logger.log(Level.WARNING, Bundle.GoogleTranslatorSettingsPanel_errorMessage_translationFailure(), ex); + } + } }//GEN-LAST:event_testButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables From 938d0826d7e08b68d32e8f8a7a00520e4997b6ce Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Wed, 5 Jun 2019 12:56:49 -0400 Subject: [PATCH 047/106] 5061 address easy to fix codacy issues --- .../translators/BingTranslator.java | 7 +++---- .../translators/BingTranslatorTest.java | 14 +++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java index 775cdf3588..4c2526ab9b 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java @@ -121,8 +121,8 @@ public class BingTranslator implements TextTranslator { try { String response = postTranslationRequest(toTranslate); return parseJSONResponse(response); - } catch (Throwable e) { - throw new TranslationException(e.getMessage()); + } catch (IOException | TranslationException ex) { + throw new TranslationException("Exception while attempting to translate using BingTranslator", ex); } } @@ -167,8 +167,7 @@ public class BingTranslator implements TextTranslator { JsonObject response0 = responses.get(0).getAsJsonObject(); JsonArray translations = response0.getAsJsonArray("translations"); JsonObject translation0 = translations.get(0).getAsJsonObject(); - String text = translation0.get("text").getAsString(); - return text; + return translation0.get("text").getAsString(); } catch (IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException e) { throw new TranslationException("JSON text does not match Bing Translator scheme: " + e); } diff --git a/Core/test/unit/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorTest.java b/Core/test/unit/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorTest.java index 48c29543d3..25c78ba049 100644 --- a/Core/test/unit/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorTest.java +++ b/Core/test/unit/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorTest.java @@ -18,13 +18,13 @@ */ package org.sleuthkit.autopsy.texttranslation.translators; -import java.io.IOException; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; +//import java.io.IOException; +//import org.junit.After; +//import org.junit.AfterClass; +//import org.junit.Before; +//import org.junit.BeforeClass; import org.junit.Test; -import static org.junit.Assert.*; +//import static org.junit.Assert.*; /** * Tests for the BingTranslator translation service, these tests have been @@ -33,7 +33,7 @@ import static org.junit.Assert.*; public class BingTranslatorTest { @Test - public void testTranslate() throws Exception { + public void testTranslate() { // BingTranslator translator = new BingTranslator(); // String input = "gato"; // String expectedTranslation = "cat"; From 1741726732129caa9c173ec466e3af840096ee98 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Wed, 5 Jun 2019 14:37:44 -0400 Subject: [PATCH 048/106] Checkout point, major functionality implemented --- .../autopsy/contentviewers/Bundle.properties | 4 +- .../contentviewers/Bundle.properties-MERGED | 7 +- .../contentviewers/MediaViewImagePanel.form | 76 +--- .../contentviewers/MediaViewImagePanel.java | 423 +++++++++++------- .../imagetagging/ImageTagsGroup.java | 10 + .../imagetagging/ImageTagsUtil.java | 48 ++ 6 files changed, 343 insertions(+), 225 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtil.java diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index 0f5f1e841e..a588e6eabb 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -89,6 +89,4 @@ MediaPlayerPanel.VolumeIcon.text=\ \ \ \ \ Volume MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00 MediaPlayerPanel.playButton.text=\u25ba MediaPlayerPanel.infoLabel.text=No Errors -MediaViewImagePanel.deleteTagButton.text=Delete Tag -MediaViewImagePanel.createTagButton.text=Create Tag -MediaViewImagePanel.showTagsButton.text=Hide Tags +MediaViewImagePanel.tagsMenu.text_1=Tags Menu diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index f04db7f709..10bc33a767 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -44,7 +44,10 @@ MediaPlayerPanel.timeFormat=%02d:%02d:%02d MediaPlayerPanel.unknownTime=Unknown MediaViewImagePanel.errorLabel.OOMText=Could not load file into Media View: insufficent memory. MediaViewImagePanel.errorLabel.text=Could not load file into Media View. +MediaViewImagePanel.exportSaveText=Save MediaViewImagePanel.externalViewerButton.text=Open in External Viewer Ctrl+E +MediaViewImagePanel.successfulExport=Tagged image was successfully saved. +MediaViewImagePanel.unsuccessfulExport=Unable to export tagged image to disk. MediaViewVideoPanel.pauseButton.text=\u25ba MediaViewVideoPanel.progressLabel.text=00:00 MediaViewVideoPanel.infoLabel.text=info @@ -151,9 +154,7 @@ MediaPlayerPanel.VolumeIcon.text=\ \ \ \ \ Volume MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00 MediaPlayerPanel.playButton.text=\u25ba MediaPlayerPanel.infoLabel.text=No Errors -MediaViewImagePanel.deleteTagButton.text=Delete Tag -MediaViewImagePanel.createTagButton.text=Create Tag -MediaViewImagePanel.showTagsButton.text=Hide Tags +MediaViewImagePanel.tagsMenu.text_1=Tags Menu # {0} - tableName SQLiteViewer.readTable.errorText=Error getting rows for table: {0} # {0} - tableName diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form index fe7843bf7a..b157f7a033 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.form @@ -1,20 +1,6 @@
- - - - - - - - - - - - - - @@ -33,7 +19,7 @@ - + @@ -226,70 +212,30 @@ - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - - + - - + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index 1e039d614a..a4bf52cfd4 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -20,8 +20,15 @@ package org.sleuthkit.autopsy.contentviewers; import java.awt.EventQueue; import java.awt.event.ActionEvent; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import static java.util.Objects.nonNull; @@ -30,6 +37,7 @@ import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.stream.Collectors; import javafx.application.Platform; +import javafx.collections.ListChangeListener.Change; import javafx.concurrent.Task; import javafx.embed.swing.JFXPanel; import javafx.geometry.Pos; @@ -49,8 +57,15 @@ import javafx.scene.transform.Rotate; import javafx.scene.transform.Scale; import javafx.scene.transform.Translate; import javax.imageio.ImageIO; +import javax.swing.JFileChooser; +import javafx.scene.Node; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JSeparator; import javax.swing.SwingUtilities; +import org.apache.commons.io.FilenameUtils; import org.controlsfx.control.MaskerPane; import org.openide.util.NbBundle; import org.python.google.common.collect.Lists; @@ -61,6 +76,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager; import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager.ContentViewerTag; import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager.SerializationException; +import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsUtil; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagControls; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagCreator; @@ -99,6 +115,14 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private final ProgressBar progressBar = new ProgressBar(); private final MaskerPane maskerPane = new MaskerPane(); + private final JPopupMenu popupMenu = new JPopupMenu(); + private final JMenuItem createTag; + private final JMenuItem deleteTag; + private final JMenuItem hideTags; + private final JMenuItem exportTags; + + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private double zoomRatio; private double rotation; // Can be 0, 90, 180, and 270. @@ -134,49 +158,138 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan public MediaViewImagePanel() { initComponents(); fxInited = org.sleuthkit.autopsy.core.Installer.isJavaFxInited(); + createTag = new JMenuItem("Create"); + createTag.addActionListener((event) -> createTag()); + createTag.setToolTipText("You may drag anywhere on the image after selecting this option."); + popupMenu.add(createTag); + + popupMenu.add(new JSeparator()); // SEPARATOR + + deleteTag = new JMenuItem("Delete"); + deleteTag.addActionListener((event) -> deleteTag()); + deleteTag.setToolTipText("Delete the selected tag."); + popupMenu.add(deleteTag); + + popupMenu.add(new JSeparator()); // SEPARATOR + + hideTags = new JMenuItem("Hide"); + hideTags.addActionListener((event) -> showOrHideTags()); + hideTags.setToolTipText("Hide the tags on this image."); + popupMenu.add(hideTags); + + popupMenu.add(new JSeparator()); // SEPARATOR + + exportTags = new JMenuItem("Export"); + exportTags.addActionListener((event) -> exportTags()); + exportTags.setToolTipText("Save the image with tags applied."); + popupMenu.add(exportTags); + + popupMenu.setPopupSize(300, 150); + if (fxInited) { - Platform.runLater(() -> { - - // build jfx ui (we could do this in FXML?) - fxImageView = new ImageView(); // will hold image - masterGroup = new Group(fxImageView); - tagsGroup = new ImageTagsGroup(fxImageView); - masterGroup.getChildren().add(tagsGroup); - deleteTagButton.setEnabled(false); - - //Update buttons when users select (or unselect) image tags. - tagsGroup.addFocusChangeListener((event) -> { - if (event.getPropertyName().equals(ImageTagControls.NOT_FOCUSED.getName())) { - deleteTagButton.setEnabled(false); - if (DisplayOptions.HIDE_TAGS.getName().equals(showTagsButton.getText())) { - createTagButton.setEnabled(true); + Platform.runLater(new Runnable() { + @Override + public void run() { + // build jfx ui (we could do this in FXML?) + fxImageView = new ImageView(); // will hold image + masterGroup = new Group(fxImageView); + tagsGroup = new ImageTagsGroup(fxImageView); + tagsGroup.getChildren().addListener((Change c) -> { + if (c.getList().isEmpty()) { + pcs.firePropertyChange(new PropertyChangeEvent(this, + "state", null, State.EMPTY)); } - } else if (event.getPropertyName().equals(ImageTagControls.FOCUSED.getName())) { - deleteTagButton.setEnabled(true); - createTagButton.setEnabled(false); - if (masterGroup.getChildren().contains(imageTagCreator)) { - imageTagCreator.disconnect(); - masterGroup.getChildren().remove(imageTagCreator); + }); + + pcs.addPropertyChangeListener((event) -> { + State currentState = (State) event.getNewValue(); + switch (currentState) { + case CREATE: + createTag.setEnabled(true); + deleteTag.setEnabled(false); + hideTags.setEnabled(true); + exportTags.setEnabled(true); + break; + case SELECTED: + if (masterGroup.getChildren().contains(imageTagCreator)) { + imageTagCreator.disconnect(); + masterGroup.getChildren().remove(imageTagCreator); + } + createTag.setEnabled(false); + deleteTag.setEnabled(true); + hideTags.setEnabled(true); + exportTags.setEnabled(true); + break; + case HIDDEN: + createTag.setEnabled(false); + deleteTag.setEnabled(false); + hideTags.setEnabled(true); + hideTags.setText(DisplayOptions.SHOW_TAGS.getName()); + exportTags.setEnabled(false); + break; + case VISIBLE: + createTag.setEnabled(true); + deleteTag.setEnabled(false); + hideTags.setEnabled(true); + hideTags.setText(DisplayOptions.HIDE_TAGS.getName()); + exportTags.setEnabled(true); + break; + case DEFAULT: + case EMPTY: + if (masterGroup.getChildren().contains(imageTagCreator)) { + imageTagCreator.disconnect(); + } + createTag.setEnabled(true); + deleteTag.setEnabled(false); + hideTags.setEnabled(false); + hideTags.setText(DisplayOptions.HIDE_TAGS.getName()); + exportTags.setEnabled(false); + break; + case NONEMPTY: + createTag.setEnabled(true); + deleteTag.setEnabled(false); + hideTags.setEnabled(true); + exportTags.setEnabled(true); + break; + case DISABLE: + createTag.setEnabled(false); + deleteTag.setEnabled(false); + hideTags.setEnabled(false); + exportTags.setEnabled(false); + break; } - } - }); + }); - scrollPane = new ScrollPane(masterGroup); // scrolls and sizes imageview - scrollPane.getStyleClass().add("bg"); //NOI18N - scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED); - scrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED); + masterGroup.getChildren().add(tagsGroup); - fxPanel = new JFXPanel(); // bridge jfx-swing - Scene scene = new Scene(scrollPane); //root of jfx tree - scene.getStylesheets().add(MediaViewImagePanel.class.getResource("MediaViewImagePanel.css").toExternalForm()); //NOI18N - fxPanel.setScene(scene); + //Update buttons when users select (or unselect) image tags. + tagsGroup.addFocusChangeListener((event) -> { + if (event.getPropertyName().equals(ImageTagControls.NOT_FOCUSED.getName())) { + pcs.firePropertyChange(new PropertyChangeEvent(this, + "state", null, State.CREATE)); + } else if (event.getPropertyName().equals(ImageTagControls.FOCUSED.getName())) { + pcs.firePropertyChange(new PropertyChangeEvent(this, + "state", null, State.SELECTED)); + } + }); - fxImageView.setSmooth(true); - fxImageView.setCache(true); + scrollPane = new ScrollPane(masterGroup); // scrolls and sizes imageview + scrollPane.getStyleClass().add("bg"); //NOI18N + scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED); + scrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED); - EventQueue.invokeLater(() -> { - add(fxPanel);//add jfx ui to JPanel - }); + fxPanel = new JFXPanel(); // bridge jfx-swing + Scene scene = new Scene(scrollPane); //root of jfx tree + scene.getStylesheets().add(MediaViewImagePanel.class.getResource("MediaViewImagePanel.css").toExternalForm()); //NOI18N + fxPanel.setScene(scene); + + fxImageView.setSmooth(true); + fxImageView.setCache(true); + + EventQueue.invokeLater(() -> { + add(fxPanel);//add jfx ui to JPanel + }); + } }); } } @@ -192,14 +305,9 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan Platform.runLater(() -> { fxImageView.setViewport(new Rectangle2D(0, 0, 0, 0)); fxImageView.setImage(null); + pcs.firePropertyChange(new PropertyChangeEvent(this, + "state", null, State.DEFAULT)); masterGroup.getChildren().clear(); - if (imageTagCreator != null) { - imageTagCreator.disconnect(); - } - showTagsButton.setText(DisplayOptions.HIDE_TAGS.getName()); - showTagsButton.setEnabled(true); - createTagButton.setEnabled(true); - deleteTagButton.setEnabled(false); scrollPane.setContent(null); scrollPane.setContent(masterGroup); }); @@ -266,6 +374,10 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan List> contentViewerTags = getContentViewerTags(tags); //Add all image tags tagsGroup = buildImageTagsGroup(contentViewerTags); + if(!tagsGroup.getChildren().isEmpty()) { + pcs.firePropertyChange(new PropertyChangeEvent(this, + "state", null, State.NONEMPTY)); + } } catch (TskCoreException | NoCurrentCaseException ex) { LOGGER.log(Level.WARNING, "Could not retrieve image tags for file in case db", ex); //NON-NLS } @@ -396,8 +508,6 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan // //GEN-BEGIN:initComponents private void initComponents() { - jPopupMenu1 = new javax.swing.JPopupMenu(); - jPopupMenu2 = new javax.swing.JPopupMenu(); toolbar = new javax.swing.JToolBar(); rotationTextField = new javax.swing.JTextField(); rotateLeftButton = new javax.swing.JButton(); @@ -410,11 +520,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan zoomResetButton = new javax.swing.JButton(); filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0)); filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0)); - createTagButton = new javax.swing.JButton(); - jSeparator4 = new javax.swing.JToolBar.Separator(); - deleteTagButton = new javax.swing.JButton(); - jSeparator3 = new javax.swing.JToolBar.Separator(); - showTagsButton = new javax.swing.JButton(); + jPanel1 = new javax.swing.JPanel(); + tagsMenu = new javax.swing.JButton(); setBackground(new java.awt.Color(0, 0, 0)); addComponentListener(new java.awt.event.ComponentAdapter() { @@ -520,52 +627,21 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan toolbar.add(zoomResetButton); toolbar.add(filler1); toolbar.add(filler2); + toolbar.add(jPanel1); - org.openide.awt.Mnemonics.setLocalizedText(createTagButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.createTagButton.text")); // NOI18N - createTagButton.setFocusable(false); - createTagButton.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - createTagButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - createTagButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - createTagButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - createTagButtonActionPerformed(evt); + org.openide.awt.Mnemonics.setLocalizedText(tagsMenu, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.tagsMenu.text_1")); // NOI18N + tagsMenu.setFocusable(false); + tagsMenu.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + tagsMenu.setMaximumSize(new java.awt.Dimension(75, 21)); + tagsMenu.setMinimumSize(new java.awt.Dimension(75, 21)); + tagsMenu.setPreferredSize(new java.awt.Dimension(75, 21)); + tagsMenu.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + tagsMenu.addMouseListener(new java.awt.event.MouseAdapter() { + public void mousePressed(java.awt.event.MouseEvent evt) { + tagsMenuMousePressed(evt); } }); - toolbar.add(createTagButton); - toolbar.add(jSeparator4); - - org.openide.awt.Mnemonics.setLocalizedText(deleteTagButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.deleteTagButton.text")); // NOI18N - deleteTagButton.setFocusable(false); - deleteTagButton.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - deleteTagButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - deleteTagButton.setMaximumSize(new java.awt.Dimension(61, 21)); - deleteTagButton.setMinimumSize(new java.awt.Dimension(61, 21)); - deleteTagButton.setPreferredSize(new java.awt.Dimension(61, 21)); - deleteTagButton.setRequestFocusEnabled(false); - deleteTagButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - deleteTagButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - deleteTagButtonActionPerformed(evt); - } - }); - toolbar.add(deleteTagButton); - toolbar.add(jSeparator3); - - org.openide.awt.Mnemonics.setLocalizedText(showTagsButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.showTagsButton.text")); // NOI18N - showTagsButton.setFocusable(false); - showTagsButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - showTagsButton.setMaximumSize(new java.awt.Dimension(61, 21)); - showTagsButton.setMinimumSize(new java.awt.Dimension(61, 21)); - showTagsButton.setOpaque(false); - showTagsButton.setPreferredSize(new java.awt.Dimension(61, 21)); - showTagsButton.setRolloverEnabled(false); - showTagsButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - showTagsButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - showTagsButtonActionPerformed(evt); - } - }); - toolbar.add(showTagsButton); + toolbar.add(tagsMenu); add(toolbar); }// //GEN-END:initComponents @@ -610,7 +686,10 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan updateView(); }//GEN-LAST:event_formComponentResized - private void deleteTagButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteTagButtonActionPerformed + /** + * + */ + private void deleteTag() { Platform.runLater(() -> { ImageTag tagInFocus = tagsGroup.getFocus(); //Null should not be expected, but just as a safetly precaution @@ -631,40 +710,41 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan scrollPane.setCursor(Cursor.DEFAULT); }); - deleteTagButton.setEnabled(false); - createTagButton.setEnabled(true); - }//GEN-LAST:event_deleteTagButtonActionPerformed + pcs.firePropertyChange(new PropertyChangeEvent(this, + "state", null, State.CREATE)); + } - private void createTagButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createTagButtonActionPerformed - createTagButton.setEnabled(false); - showTagsButton.setEnabled(false); + /** + * + */ + private void createTag() { + pcs.firePropertyChange(new PropertyChangeEvent(this, + "state", null, State.DISABLE)); imageTagCreator = new ImageTagCreator(fxImageView); PropertyChangeListener newTagListener = (event) -> { - SwingUtilities.invokeLater(() -> { ImageTagRegion tag = (ImageTagRegion) event.getNewValue(); //Ask the user for tag name and comment TagNameAndComment result = GetTagNameAndCommentDialog.doDialog(); - if (result == null) { - createTagButton.setEnabled(true); - showTagsButton.setEnabled(true); - return; + if (result != null) { + //Persist and build image tag + Platform.runLater(() -> { + try { + scrollPane.setCursor(Cursor.WAIT); + ContentViewerTag contentViewerTag = storeImageTag(tag, result); + ImageTag imageTag = buildImageTag(contentViewerTag); + tagsGroup.getChildren().add(imageTag); + } catch (TskCoreException | SerializationException | NoCurrentCaseException ex) { + LOGGER.log(Level.WARNING, "Could not save new image tag in case db", ex); //NON-NLS + } + + scrollPane.setCursor(Cursor.DEFAULT); + }); } - //Persist and build image tag - Platform.runLater(() -> { - try { - scrollPane.setCursor(Cursor.WAIT); - ContentViewerTag contentViewerTag = storeImageTag(tag, result); - ImageTag imageTag = buildImageTag(contentViewerTag); - tagsGroup.getChildren().add(imageTag); - } catch (TskCoreException | SerializationException | NoCurrentCaseException ex) { - LOGGER.log(Level.WARNING, "Could not save new image tag in case db", ex); //NON-NLS - } - - scrollPane.setCursor(Cursor.DEFAULT); - }); + pcs.firePropertyChange(new PropertyChangeEvent(this, + "state", null, State.CREATE)); }); //Remove image tag creator from panel @@ -676,11 +756,11 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan imageTagCreator.addNewTagListener(newTagListener); Platform.runLater(() -> masterGroup.getChildren().add(imageTagCreator)); - }//GEN-LAST:event_createTagButtonActionPerformed + } /** * Creates an ImageTag instance from the ContentViewerTag. - * + * * @param contentViewerTag * @return */ @@ -710,48 +790,77 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan */ private ContentViewerTag storeImageTag(ImageTagRegion data, TagNameAndComment result) throws TskCoreException, SerializationException, NoCurrentCaseException { - try { - scrollPane.setCursor(Cursor.WAIT); - ContentTag contentTag = Case.getCurrentCaseThrows().getServices().getTagsManager() - .addContentTag(file, result.getTagName(), result.getComment()); - ContentViewerTag contentViewerTag = ContentViewerTagManager.saveTag(contentTag, data); - return contentViewerTag; - } finally { - scrollPane.setCursor(Cursor.DEFAULT); - createTagButton.setEnabled(true); - showTagsButton.setEnabled(true); - } + scrollPane.setCursor(Cursor.WAIT); + ContentTag contentTag = Case.getCurrentCaseThrows().getServices().getTagsManager() + .addContentTag(file, result.getTagName(), result.getComment()); + ContentViewerTag contentViewerTag = ContentViewerTagManager.saveTag(contentTag, data); + scrollPane.setCursor(Cursor.DEFAULT); + return contentViewerTag; } - private void showTagsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showTagsButtonActionPerformed + /** + * + */ + private void showOrHideTags() { Platform.runLater(() -> { - if (DisplayOptions.HIDE_TAGS.getName().equals(showTagsButton.getText())) { + if (DisplayOptions.HIDE_TAGS.getName().equals(hideTags.getText())) { //Temporarily remove the tags group and update buttons masterGroup.getChildren().remove(tagsGroup); - showTagsButton.setText(DisplayOptions.SHOW_TAGS.getName()); - createTagButton.setEnabled(false); - deleteTagButton.setEnabled(false); + hideTags.setText(DisplayOptions.SHOW_TAGS.getName()); + tagsGroup.clearFocus(); + pcs.firePropertyChange(new PropertyChangeEvent(this, + "state", null, State.HIDDEN)); } else { //Add tags group back in and update buttons masterGroup.getChildren().add(tagsGroup); - showTagsButton.setText(DisplayOptions.HIDE_TAGS.getName()); - if (tagsGroup.getFocus() != null) { - createTagButton.setEnabled(false); - deleteTagButton.setEnabled(true); - } else { - createTagButton.setEnabled(true); - deleteTagButton.setEnabled(false); - } + hideTags.setText(DisplayOptions.HIDE_TAGS.getName()); + pcs.firePropertyChange(new PropertyChangeEvent(this, + "state", null, State.VISIBLE)); } }); - }//GEN-LAST:event_showTagsButtonActionPerformed + } + + @NbBundle.Messages({ + "MediaViewImagePanel.exportSaveText=Save", + "MediaViewImagePanel.successfulExport=Tagged image was successfully saved.", + "MediaViewImagePanel.unsuccessfulExport=Unable to export tagged image to disk." + }) + private void exportTags() { + tagsGroup.clearFocus(); + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + int returnVal = fileChooser.showDialog(this, Bundle.MediaViewImagePanel_exportSaveText()); + if (returnVal == JFileChooser.APPROVE_OPTION) { + Platform.runLater(() -> { + try { + List tags = Case.getCurrentCase().getServices() + .getTagsManager().getContentTagsByContent(file); + List> contentViewerTags = getContentViewerTags(tags); + Collection regions = contentViewerTags.stream() + .map(cvTag -> cvTag.getDetails()).collect(Collectors.toList()); + byte[] jpgImage = ImageTagsUtil.exportTags(file, regions, ".jpg"); + Path output = Paths.get(fileChooser.getSelectedFile().getPath(), + FilenameUtils.getBaseName(file.getName()) + "-with_tags.jpg"); + Files.write(output, jpgImage); + JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_successfulExport()); + } catch (TskCoreException | NoCurrentCaseException | IOException ex) { + LOGGER.log(Level.WARNING, "Unable to export tagged image to disk", ex); //NON-NLS + JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_unsuccessfulExport()); + } + }); + } + } + + private void tagsMenuMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_tagsMenuMousePressed + popupMenu.show(tagsMenu, -300 + tagsMenu.getWidth(), tagsMenu.getHeight() + 3); + }//GEN-LAST:event_tagsMenuMousePressed /** * Display states for the show/hide tags button. */ enum DisplayOptions { - HIDE_TAGS("Hide Tags"), - SHOW_TAGS("Show Tags"); + HIDE_TAGS("Hide"), + SHOW_TAGS("Show"); private final String name; @@ -764,21 +873,27 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan } } + enum State { + HIDDEN, + VISIBLE, + SELECTED, + CREATE, + EMPTY, + NONEMPTY, + DEFAULT, + DISABLE; + } + // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton createTagButton; - private javax.swing.JButton deleteTagButton; private javax.swing.Box.Filler filler1; private javax.swing.Box.Filler filler2; - private javax.swing.JPopupMenu jPopupMenu1; - private javax.swing.JPopupMenu jPopupMenu2; + private javax.swing.JPanel jPanel1; private javax.swing.JToolBar.Separator jSeparator1; private javax.swing.JToolBar.Separator jSeparator2; - private javax.swing.JToolBar.Separator jSeparator3; - private javax.swing.JToolBar.Separator jSeparator4; private javax.swing.JButton rotateLeftButton; private javax.swing.JButton rotateRightButton; private javax.swing.JTextField rotationTextField; - private javax.swing.JButton showTagsButton; + private javax.swing.JButton tagsMenu; private javax.swing.JToolBar toolbar; private javax.swing.JButton zoomInButton; private javax.swing.JButton zoomOutButton; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java index 828b083d80..e226c9f2a3 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java @@ -92,6 +92,16 @@ public final class ImageTagsGroup extends Group { public ImageTag getFocus() { return currentFocus; } + + /** + * Clears the current focus + */ + public void clearFocus() { + if(currentFocus != null) { + resetFocus(currentFocus); + currentFocus = null; + } + } /** * Notifies the logical image tag that it is no longer in focus. diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtil.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtil.java new file mode 100755 index 0000000000..391a8700ea --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtil.java @@ -0,0 +1,48 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.contentviewers.imagetagging; + +import java.util.Collection; +import org.opencv.core.Core; +import org.opencv.core.Mat; +import org.opencv.core.MatOfByte; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.highgui.Highgui; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * + * @author dsmyda + */ +public class ImageTagsUtil { + + public static byte[] exportTags(AbstractFile file, Collection tagRegions, String outputEncoding) throws TskCoreException { + byte[] imageInMemory = new byte[(int) file.getSize()]; + file.read(imageInMemory, 0, file.getSize()); + Mat originalImage = Highgui.imdecode(new MatOfByte(imageInMemory), Highgui.IMREAD_UNCHANGED); + + tagRegions.forEach((region) -> { + Core.rectangle( + originalImage, //Matrix obj of the image + new Point(region.getX(), region.getY()), //p1 + new Point(region.getX() + region.getWidth(), region.getY() + region.getHeight()), //p2 + new Scalar(0, 0, 255), //Scalar object for color + (int) Math.rint(region.getStrokeThickness()) + ); + }); + + MatOfByte matOfByte = new MatOfByte(); + Highgui.imencode(outputEncoding, originalImage, matOfByte); + + originalImage.release(); + byte[] output = matOfByte.toArray(); + matOfByte.release(); + + return output; + } +} From 366e784a317f920960e75e16a5afd1e6ec4e6893 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Wed, 5 Jun 2019 15:49:08 -0400 Subject: [PATCH 049/106] Made filechooser remember selection, fixed some enable/disable bugs, removed tooltips --- .../contentviewers/Bundle.properties-MERGED | 1 + .../contentviewers/MediaViewImagePanel.java | 112 +++++++++++------- .../imagetagging/ImageTagsGroup.java | 2 +- 3 files changed, 73 insertions(+), 42 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index 10bc33a767..2779cef39a 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -46,6 +46,7 @@ MediaViewImagePanel.errorLabel.OOMText=Could not load file into Media View: insu MediaViewImagePanel.errorLabel.text=Could not load file into Media View. MediaViewImagePanel.exportSaveText=Save MediaViewImagePanel.externalViewerButton.text=Open in External Viewer Ctrl+E +MediaViewImagePanel.fileChooserTitle=Choose a directory to save the image MediaViewImagePanel.successfulExport=Tagged image was successfully saved. MediaViewImagePanel.unsuccessfulExport=Unable to export tagged image to disk. MediaViewVideoPanel.pauseButton.text=\u25ba diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index a4bf52cfd4..e1c0e70281 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -65,6 +65,7 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JSeparator; import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; import org.apache.commons.io.FilenameUtils; import org.controlsfx.control.MaskerPane; import org.openide.util.NbBundle; @@ -121,6 +122,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private final JMenuItem hideTags; private final JMenuItem exportTags; + private final JFileChooser exportChooser; + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private double zoomRatio; @@ -158,30 +161,31 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan public MediaViewImagePanel() { initComponents(); fxInited = org.sleuthkit.autopsy.core.Installer.isJavaFxInited(); + + exportChooser = new JFileChooser(); + exportChooser.setDialogTitle(Bundle.MediaViewImagePanel_fileChooserTitle()); + + //Build popupMenu when Tags Menu button is pressed. createTag = new JMenuItem("Create"); createTag.addActionListener((event) -> createTag()); - createTag.setToolTipText("You may drag anywhere on the image after selecting this option."); popupMenu.add(createTag); - popupMenu.add(new JSeparator()); // SEPARATOR + popupMenu.add(new JSeparator()); deleteTag = new JMenuItem("Delete"); deleteTag.addActionListener((event) -> deleteTag()); - deleteTag.setToolTipText("Delete the selected tag."); popupMenu.add(deleteTag); - popupMenu.add(new JSeparator()); // SEPARATOR + popupMenu.add(new JSeparator()); hideTags = new JMenuItem("Hide"); hideTags.addActionListener((event) -> showOrHideTags()); - hideTags.setToolTipText("Hide the tags on this image."); popupMenu.add(hideTags); - popupMenu.add(new JSeparator()); // SEPARATOR + popupMenu.add(new JSeparator()); exportTags = new JMenuItem("Export"); exportTags.addActionListener((event) -> exportTags()); - exportTags.setToolTipText("Save the image with tags applied."); popupMenu.add(exportTags); popupMenu.setPopupSize(300, 150); @@ -201,6 +205,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan } }); + //Respond to state events by enabling/disabling the correct + //buttons. pcs.addPropertyChangeListener((event) -> { State currentState = (State) event.getNewValue(); switch (currentState) { @@ -265,8 +271,17 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan //Update buttons when users select (or unselect) image tags. tagsGroup.addFocusChangeListener((event) -> { if (event.getPropertyName().equals(ImageTagControls.NOT_FOCUSED.getName())) { - pcs.firePropertyChange(new PropertyChangeEvent(this, + if (masterGroup.getChildren().contains(imageTagCreator)) { + return; + } + + if(tagsGroup.getChildren().isEmpty()) { + pcs.firePropertyChange(new PropertyChangeEvent(this, + "state", null, State.EMPTY)); + } else { + pcs.firePropertyChange(new PropertyChangeEvent(this, "state", null, State.CREATE)); + } } else if (event.getPropertyName().equals(ImageTagControls.FOCUSED.getName())) { pcs.firePropertyChange(new PropertyChangeEvent(this, "state", null, State.SELECTED)); @@ -374,9 +389,9 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan List> contentViewerTags = getContentViewerTags(tags); //Add all image tags tagsGroup = buildImageTagsGroup(contentViewerTags); - if(!tagsGroup.getChildren().isEmpty()) { + if (!tagsGroup.getChildren().isEmpty()) { pcs.firePropertyChange(new PropertyChangeEvent(this, - "state", null, State.NONEMPTY)); + "state", null, State.NONEMPTY)); } } catch (TskCoreException | NoCurrentCaseException ex) { LOGGER.log(Level.WARNING, "Could not retrieve image tags for file in case db", ex); //NON-NLS @@ -687,7 +702,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan }//GEN-LAST:event_formComponentResized /** - * + * Deletes the selected tag when the Delete button is pressed in the Tag + * Menu. */ private void deleteTag() { Platform.runLater(() -> { @@ -715,7 +731,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan } /** - * + * Enables create tag logic when the Create button is pressed in the Tags + * Menu. */ private void createTag() { pcs.firePropertyChange(new PropertyChangeEvent(this, @@ -791,15 +808,19 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private ContentViewerTag storeImageTag(ImageTagRegion data, TagNameAndComment result) throws TskCoreException, SerializationException, NoCurrentCaseException { scrollPane.setCursor(Cursor.WAIT); - ContentTag contentTag = Case.getCurrentCaseThrows().getServices().getTagsManager() - .addContentTag(file, result.getTagName(), result.getComment()); - ContentViewerTag contentViewerTag = ContentViewerTagManager.saveTag(contentTag, data); - scrollPane.setCursor(Cursor.DEFAULT); - return contentViewerTag; + try { + ContentTag contentTag = Case.getCurrentCaseThrows().getServices().getTagsManager() + .addContentTag(file, result.getTagName(), result.getComment()); + ContentViewerTag contentViewerTag = ContentViewerTagManager.saveTag(contentTag, data); + return contentViewerTag; + } finally { + scrollPane.setCursor(Cursor.DEFAULT); + } } /** - * + * Hides or show tags when the Hide or Show button is pressed in the Tags + * Menu. */ private void showOrHideTags() { Platform.runLater(() -> { @@ -809,13 +830,13 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan hideTags.setText(DisplayOptions.SHOW_TAGS.getName()); tagsGroup.clearFocus(); pcs.firePropertyChange(new PropertyChangeEvent(this, - "state", null, State.HIDDEN)); + "state", null, State.HIDDEN)); } else { //Add tags group back in and update buttons masterGroup.getChildren().add(tagsGroup); hideTags.setText(DisplayOptions.HIDE_TAGS.getName()); - pcs.firePropertyChange(new PropertyChangeEvent(this, - "state", null, State.VISIBLE)); + pcs.firePropertyChange(new PropertyChangeEvent(this, + "state", null, State.VISIBLE)); } }); } @@ -823,31 +844,36 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan @NbBundle.Messages({ "MediaViewImagePanel.exportSaveText=Save", "MediaViewImagePanel.successfulExport=Tagged image was successfully saved.", - "MediaViewImagePanel.unsuccessfulExport=Unable to export tagged image to disk." + "MediaViewImagePanel.unsuccessfulExport=Unable to export tagged image to disk.", + "MediaViewImagePanel.fileChooserTitle=Choose a directory to save the image" }) private void exportTags() { tagsGroup.clearFocus(); - JFileChooser fileChooser = new JFileChooser(); - fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - int returnVal = fileChooser.showDialog(this, Bundle.MediaViewImagePanel_exportSaveText()); + exportChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + int returnVal = exportChooser.showDialog(this, Bundle.MediaViewImagePanel_exportSaveText()); if (returnVal == JFileChooser.APPROVE_OPTION) { - Platform.runLater(() -> { - try { - List tags = Case.getCurrentCase().getServices() - .getTagsManager().getContentTagsByContent(file); - List> contentViewerTags = getContentViewerTags(tags); - Collection regions = contentViewerTags.stream() - .map(cvTag -> cvTag.getDetails()).collect(Collectors.toList()); - byte[] jpgImage = ImageTagsUtil.exportTags(file, regions, ".jpg"); - Path output = Paths.get(fileChooser.getSelectedFile().getPath(), - FilenameUtils.getBaseName(file.getName()) + "-with_tags.jpg"); - Files.write(output, jpgImage); - JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_successfulExport()); - } catch (TskCoreException | NoCurrentCaseException | IOException ex) { - LOGGER.log(Level.WARNING, "Unable to export tagged image to disk", ex); //NON-NLS - JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_unsuccessfulExport()); + exportChooser.setCurrentDirectory(exportChooser.getSelectedFile()); + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + try { + List tags = Case.getCurrentCase().getServices() + .getTagsManager().getContentTagsByContent(file); + List> contentViewerTags = getContentViewerTags(tags); + Collection regions = contentViewerTags.stream() + .map(cvTag -> cvTag.getDetails()).collect(Collectors.toList()); + byte[] jpgImage = ImageTagsUtil.exportTags(file, regions, ".jpg"); + Path output = Paths.get(exportChooser.getSelectedFile().getPath(), + FilenameUtils.getBaseName(file.getName()) + "-with_tags.jpg"); //NON-NLS + Files.write(output, jpgImage); + JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_successfulExport()); + } catch (TskCoreException | NoCurrentCaseException | IOException ex) { + LOGGER.log(Level.WARNING, "Unable to export tagged image to disk", ex); //NON-NLS + JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_unsuccessfulExport()); + } + return null; } - }); + }.execute(); } } @@ -873,6 +899,10 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan } } + /** + * Different states that the content viewer can be in. These states drive + * which buttons are enabled for tagging. + */ enum State { HIDDEN, VISIBLE, diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java index e226c9f2a3..fcbbb145bd 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java @@ -52,11 +52,11 @@ public final class ImageTagsGroup extends Group { if (currentFocus != null) { currentFocus.getEventDispatcher().dispatchEvent( new Event(ImageTagControls.NOT_FOCUSED), NO_OP_CHAIN); - currentFocus = null; } this.pcs.firePropertyChange(new PropertyChangeEvent(this, ImageTagControls.NOT_FOCUSED.getName(), currentFocus, null)); + currentFocus = null; }); //Set the focus of selected tag From 0541a3368b12007934ec9fc16e7bf9903dcf7c44 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Wed, 5 Jun 2019 16:16:13 -0400 Subject: [PATCH 050/106] Fixed some codacy suggestions, added comments, minor changes --- .../contentviewers/Bundle.properties-MERGED | 2 +- .../contentviewers/MediaViewImagePanel.java | 115 +++++++++--------- ...ageTagsUtil.java => ImageTagsUtility.java} | 34 ++++-- 3 files changed, 85 insertions(+), 66 deletions(-) rename Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/{ImageTagsUtil.java => ImageTagsUtility.java} (58%) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index 2779cef39a..d13fe56608 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -46,7 +46,7 @@ MediaViewImagePanel.errorLabel.OOMText=Could not load file into Media View: insu MediaViewImagePanel.errorLabel.text=Could not load file into Media View. MediaViewImagePanel.exportSaveText=Save MediaViewImagePanel.externalViewerButton.text=Open in External Viewer Ctrl+E -MediaViewImagePanel.fileChooserTitle=Choose a directory to save the image +MediaViewImagePanel.fileChooserTitle=Choose a save location MediaViewImagePanel.successfulExport=Tagged image was successfully saved. MediaViewImagePanel.unsuccessfulExport=Unable to export tagged image to disk. MediaViewVideoPanel.pauseButton.text=\u25ba diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index e1c0e70281..b4d1202f15 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -77,7 +77,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager; import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager.ContentViewerTag; import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager.SerializationException; -import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsUtil; +import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsUtility; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagControls; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagCreator; @@ -102,7 +102,7 @@ import org.sleuthkit.datamodel.TskCoreException; class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPanel { private static final Image EXTERNAL = new Image(MediaViewImagePanel.class.getResource("/org/sleuthkit/autopsy/images/external.png").toExternalForm()); - private final Logger LOGGER = Logger.getLogger(MediaViewImagePanel.class.getName()); + private final static Logger LOGGER = Logger.getLogger(MediaViewImagePanel.class.getName()); private final boolean fxInited; @@ -117,10 +117,10 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private final MaskerPane maskerPane = new MaskerPane(); private final JPopupMenu popupMenu = new JPopupMenu(); - private final JMenuItem createTag; - private final JMenuItem deleteTag; - private final JMenuItem hideTags; - private final JMenuItem exportTags; + private final JMenuItem createTagMenuItem; + private final JMenuItem deleteTagMenuItem; + private final JMenuItem hideTagsMenuItem; + private final JMenuItem exportTagsMenuItem; private final JFileChooser exportChooser; @@ -166,27 +166,27 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan exportChooser.setDialogTitle(Bundle.MediaViewImagePanel_fileChooserTitle()); //Build popupMenu when Tags Menu button is pressed. - createTag = new JMenuItem("Create"); - createTag.addActionListener((event) -> createTag()); - popupMenu.add(createTag); + createTagMenuItem = new JMenuItem("Create"); + createTagMenuItem.addActionListener((event) -> createTag()); + popupMenu.add(createTagMenuItem); popupMenu.add(new JSeparator()); - deleteTag = new JMenuItem("Delete"); - deleteTag.addActionListener((event) -> deleteTag()); - popupMenu.add(deleteTag); + deleteTagMenuItem = new JMenuItem("Delete"); + deleteTagMenuItem.addActionListener((event) -> deleteTag()); + popupMenu.add(deleteTagMenuItem); popupMenu.add(new JSeparator()); - hideTags = new JMenuItem("Hide"); - hideTags.addActionListener((event) -> showOrHideTags()); - popupMenu.add(hideTags); + hideTagsMenuItem = new JMenuItem("Hide"); + hideTagsMenuItem.addActionListener((event) -> showOrHideTags()); + popupMenu.add(hideTagsMenuItem); popupMenu.add(new JSeparator()); - exportTags = new JMenuItem("Export"); - exportTags.addActionListener((event) -> exportTags()); - popupMenu.add(exportTags); + exportTagsMenuItem = new JMenuItem("Export"); + exportTagsMenuItem.addActionListener((event) -> exportTags()); + popupMenu.add(exportTagsMenuItem); popupMenu.setPopupSize(300, 150); @@ -211,57 +211,59 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan State currentState = (State) event.getNewValue(); switch (currentState) { case CREATE: - createTag.setEnabled(true); - deleteTag.setEnabled(false); - hideTags.setEnabled(true); - exportTags.setEnabled(true); + createTagMenuItem.setEnabled(true); + deleteTagMenuItem.setEnabled(false); + hideTagsMenuItem.setEnabled(true); + exportTagsMenuItem.setEnabled(true); break; case SELECTED: if (masterGroup.getChildren().contains(imageTagCreator)) { imageTagCreator.disconnect(); masterGroup.getChildren().remove(imageTagCreator); } - createTag.setEnabled(false); - deleteTag.setEnabled(true); - hideTags.setEnabled(true); - exportTags.setEnabled(true); + createTagMenuItem.setEnabled(false); + deleteTagMenuItem.setEnabled(true); + hideTagsMenuItem.setEnabled(true); + exportTagsMenuItem.setEnabled(true); break; case HIDDEN: - createTag.setEnabled(false); - deleteTag.setEnabled(false); - hideTags.setEnabled(true); - hideTags.setText(DisplayOptions.SHOW_TAGS.getName()); - exportTags.setEnabled(false); + createTagMenuItem.setEnabled(false); + deleteTagMenuItem.setEnabled(false); + hideTagsMenuItem.setEnabled(true); + hideTagsMenuItem.setText(DisplayOptions.SHOW_TAGS.getName()); + exportTagsMenuItem.setEnabled(false); break; case VISIBLE: - createTag.setEnabled(true); - deleteTag.setEnabled(false); - hideTags.setEnabled(true); - hideTags.setText(DisplayOptions.HIDE_TAGS.getName()); - exportTags.setEnabled(true); + createTagMenuItem.setEnabled(true); + deleteTagMenuItem.setEnabled(false); + hideTagsMenuItem.setEnabled(true); + hideTagsMenuItem.setText(DisplayOptions.HIDE_TAGS.getName()); + exportTagsMenuItem.setEnabled(true); break; case DEFAULT: case EMPTY: if (masterGroup.getChildren().contains(imageTagCreator)) { imageTagCreator.disconnect(); } - createTag.setEnabled(true); - deleteTag.setEnabled(false); - hideTags.setEnabled(false); - hideTags.setText(DisplayOptions.HIDE_TAGS.getName()); - exportTags.setEnabled(false); + createTagMenuItem.setEnabled(true); + deleteTagMenuItem.setEnabled(false); + hideTagsMenuItem.setEnabled(false); + hideTagsMenuItem.setText(DisplayOptions.HIDE_TAGS.getName()); + exportTagsMenuItem.setEnabled(false); break; case NONEMPTY: - createTag.setEnabled(true); - deleteTag.setEnabled(false); - hideTags.setEnabled(true); - exportTags.setEnabled(true); + createTagMenuItem.setEnabled(true); + deleteTagMenuItem.setEnabled(false); + hideTagsMenuItem.setEnabled(true); + exportTagsMenuItem.setEnabled(true); break; case DISABLE: - createTag.setEnabled(false); - deleteTag.setEnabled(false); - hideTags.setEnabled(false); - exportTags.setEnabled(false); + createTagMenuItem.setEnabled(false); + deleteTagMenuItem.setEnabled(false); + hideTagsMenuItem.setEnabled(false); + exportTagsMenuItem.setEnabled(false); + break; + default: break; } }); @@ -811,8 +813,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan try { ContentTag contentTag = Case.getCurrentCaseThrows().getServices().getTagsManager() .addContentTag(file, result.getTagName(), result.getComment()); - ContentViewerTag contentViewerTag = ContentViewerTagManager.saveTag(contentTag, data); - return contentViewerTag; + return ContentViewerTagManager.saveTag(contentTag, data); } finally { scrollPane.setCursor(Cursor.DEFAULT); } @@ -824,17 +825,17 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan */ private void showOrHideTags() { Platform.runLater(() -> { - if (DisplayOptions.HIDE_TAGS.getName().equals(hideTags.getText())) { + if (DisplayOptions.HIDE_TAGS.getName().equals(hideTagsMenuItem.getText())) { //Temporarily remove the tags group and update buttons masterGroup.getChildren().remove(tagsGroup); - hideTags.setText(DisplayOptions.SHOW_TAGS.getName()); + hideTagsMenuItem.setText(DisplayOptions.SHOW_TAGS.getName()); tagsGroup.clearFocus(); pcs.firePropertyChange(new PropertyChangeEvent(this, "state", null, State.HIDDEN)); } else { //Add tags group back in and update buttons masterGroup.getChildren().add(tagsGroup); - hideTags.setText(DisplayOptions.HIDE_TAGS.getName()); + hideTagsMenuItem.setText(DisplayOptions.HIDE_TAGS.getName()); pcs.firePropertyChange(new PropertyChangeEvent(this, "state", null, State.VISIBLE)); } @@ -845,7 +846,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan "MediaViewImagePanel.exportSaveText=Save", "MediaViewImagePanel.successfulExport=Tagged image was successfully saved.", "MediaViewImagePanel.unsuccessfulExport=Unable to export tagged image to disk.", - "MediaViewImagePanel.fileChooserTitle=Choose a directory to save the image" + "MediaViewImagePanel.fileChooserTitle=Choose a save location" }) private void exportTags() { tagsGroup.clearFocus(); @@ -855,14 +856,14 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan exportChooser.setCurrentDirectory(exportChooser.getSelectedFile()); new SwingWorker() { @Override - protected Void doInBackground() throws Exception { + protected Void doInBackground() { try { List tags = Case.getCurrentCase().getServices() .getTagsManager().getContentTagsByContent(file); List> contentViewerTags = getContentViewerTags(tags); Collection regions = contentViewerTags.stream() .map(cvTag -> cvTag.getDetails()).collect(Collectors.toList()); - byte[] jpgImage = ImageTagsUtil.exportTags(file, regions, ".jpg"); + byte[] jpgImage = ImageTagsUtility.exportTags(file, regions, ".jpg"); Path output = Paths.get(exportChooser.getSelectedFile().getPath(), FilenameUtils.getBaseName(file.getName()) + "-with_tags.jpg"); //NON-NLS Files.write(output, jpgImage); diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtil.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java similarity index 58% rename from Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtil.java rename to Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java index 391a8700ea..67ec058c84 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtil.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * 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.contentviewers.imagetagging; @@ -15,12 +28,17 @@ import org.opencv.highgui.Highgui; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; -/** - * - * @author dsmyda - */ -public class ImageTagsUtil { +public class ImageTagsUtility { + /** + * Embeds the tag regions into an image (represented as an AbstractFile). + * + * @param file Base Image + * @param tagRegions Tag regions to be saved into the image + * @param outputEncoding Output file type encoding (ex. .jpg, .png) + * @return output image in byte array + * @throws TskCoreException + */ public static byte[] exportTags(AbstractFile file, Collection tagRegions, String outputEncoding) throws TskCoreException { byte[] imageInMemory = new byte[(int) file.getSize()]; file.read(imageInMemory, 0, file.getSize()); From 3ef9746d2647f76d29d04dea6e21c6c7164f727d Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Thu, 6 Jun 2019 10:26:02 -0400 Subject: [PATCH 051/106] Handle empty tables. Cleanup --- .../corecomponents/Bundle.properties-MERGED | 1 + .../corecomponents/DataResultViewerTable.form | 10 ++-- .../corecomponents/DataResultViewerTable.java | 27 +++++++--- .../directorytree/Bundle.properties-MERGED | 1 + .../directorytree/ExportCSVAction.java | 50 ++++++++++++------- 5 files changed, 59 insertions(+), 30 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED index 43ceba39ba..b0e36da986 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED @@ -32,6 +32,7 @@ DataResultViewerTable.commentRenderer.noComment.toolTip=No comments found DataResultViewerTable.commentRenderer.tagComment.toolTip=Comment exists on associated tag(s) DataResultViewerTable.countRender.name=O DataResultViewerTable.countRender.toolTip=O(ccurrences) indicates the number of data sources containing the item in the Central Repository +DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export DataResultViewerTable.firstColLbl=Name DataResultViewerTable.goToPageTextField.err=Invalid page number # {0} - totalPages diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form index 87e771695b..6aeda07298 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form @@ -18,12 +18,11 @@ - - + - + - + @@ -33,7 +32,8 @@ - + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index eb5fad97f5..e6c1110038 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -77,6 +77,7 @@ 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.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo; @@ -176,6 +177,13 @@ public class DataResultViewerTable extends AbstractDataResultViewer { initComponents(); initializePagingSupport(); + + /* + * Disable the CSV export button for the common properties results + */ + if (this instanceof org.sleuthkit.autopsy.commonpropertiessearch.CommonAttributesSearchResultsViewerTable) { + exportCSVButton.setEnabled(false); + } /* * Configure the child OutlineView (explorer view) component. @@ -1352,12 +1360,11 @@ public class DataResultViewerTable extends AbstractDataResultViewer { layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 904, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() - .addComponent(exportCSVButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap() .addComponent(pageLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGap(14, 14, 14) .addComponent(pagesLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -1367,7 +1374,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer { .addComponent(gotoPageLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(exportCSVButton)) ); layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {pageNextButton, pagePrevButton}); @@ -1407,8 +1415,15 @@ public class DataResultViewerTable extends AbstractDataResultViewer { pagingSupport.gotoPage(); }//GEN-LAST:event_gotoPageTextFieldActionPerformed + @NbBundle.Messages({"DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export" + }) private void exportCSVButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCSVButtonActionPerformed - org.sleuthkit.autopsy.directorytree.ExportCSVAction.saveNodesToCSV(java.util.Arrays.asList(rootNode.getChildren().getNodes()), this); + Node currentRoot = this.getExplorerManager().getRootContext(); + if (currentRoot != null && currentRoot.getChildren().getNodesCount() > 0) { + org.sleuthkit.autopsy.directorytree.ExportCSVAction.saveNodesToCSV(java.util.Arrays.asList(currentRoot.getChildren().getNodes()), this); + } else { + MessageNotifyUtil.Message.info(Bundle.DataResultViewerTable_exportCSVButtonActionPerformed_empty()); + } }//GEN-LAST:event_exportCSVButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED index 6ec6a0e76b..f70a374bc5 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED @@ -10,6 +10,7 @@ DirectoryTreeTopComponent.componentOpened.groupDataSources.text=This case contai DirectoryTreeTopComponent.componentOpened.groupDataSources.title=Group by data source? DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module. DirectoryTreeTopComponent.resultsView.title=Listing +ExportCSV.saveNodesToCSV.empty=No data to export # {0} - Output file ExportCSV.saveNodesToCSV.fileExists=File {0} already exists ExportCSV.saveNodesToCSV.noCurrentCase=No open case available diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java index dce6244084..7fa4560afc 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2018 Basis Technology Corp. + * Copyright 2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,6 @@ import java.awt.Component; import java.awt.event.ActionEvent; import java.io.File; import java.io.BufferedWriter; -import java.io.FileWriter; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.lang.reflect.InvocationTargetException; @@ -31,10 +30,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; -import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.AbstractAction; @@ -48,14 +45,8 @@ import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor; -import org.sleuthkit.autopsy.datamodel.FileNode; -import org.sleuthkit.datamodel.AbstractFile; -import org.openide.nodes.AbstractNode; import org.openide.nodes.Node; import org.openide.nodes.Node.PropertySet; import org.openide.nodes.Node.Property; @@ -76,6 +67,12 @@ public final class ExportCSVAction extends AbstractAction { // node in the array returns a reference to the same action object from Node.getActions(boolean). private static ExportCSVAction instance; + /** + * Get an instance of the Action. See above for why + * the class is a singleton. + * + * @return the instance + */ public static synchronized ExportCSVAction getInstance() { if (null == instance) { instance = new ExportCSVAction(); @@ -104,20 +101,28 @@ public final class ExportCSVAction extends AbstractAction { saveNodesToCSV(selectedNodes, (Component)e.getSource()); } + /** + * Save the selected nodes to a CSV file + * + * @param nodesToExport the nodes to save + * @param component + */ @NbBundle.Messages({ "# {0} - Output file", "ExportCSV.saveNodesToCSV.fileExists=File {0} already exists", - "ExportCSV.saveNodesToCSV.noCurrentCase=No open case available"}) + "ExportCSV.saveNodesToCSV.noCurrentCase=No open case available", + "ExportCSV.saveNodesToCSV.empty=No data to export"}) public static void saveNodesToCSV(Collection nodesToExport, Component component) { if (nodesToExport.isEmpty()) { + MessageNotifyUtil.Message.info(Bundle.ExportCSV_saveNodesToCSV_empty()); return; } try { - + // Set up the file chooser with a default name and either the Export + // folder or the last used folder. String fileName = getDefaultOutputFileName(nodesToExport.iterator().next().getParentNode()); - JFileChooser fileChooser = new JFileChooser(); fileChooser.setCurrentDirectory(new File(getExportDirectory(Case.getCurrentCaseThrows()))); fileChooser.setSelectedFile(new File(fileName)); @@ -126,15 +131,18 @@ public final class ExportCSVAction extends AbstractAction { int returnVal = fileChooser.showSaveDialog(component); if (returnVal == JFileChooser.APPROVE_OPTION) { + // Get the file name, appending .csv if necessary File selectedFile = fileChooser.getSelectedFile(); if (!selectedFile.getName().endsWith(".csv")) { // NON-NLS selectedFile = new File(selectedFile.toString() + ".csv"); // NON-NLS } + + // Save the directory used for next time updateExportDirectory(selectedFile.getParent(), Case.getCurrentCaseThrows()); if (selectedFile.exists()) { logger.log(Level.SEVERE, "File {0} already exists", selectedFile.getAbsolutePath()); //NON-NLS - MessageNotifyUtil.Message.info(Bundle.ExportCSV_actionPerformed_fileExists(selectedFile)); + MessageNotifyUtil.Message.info(Bundle.ExportCSV_saveNodesToCSV_fileExists(selectedFile)); return; } @@ -142,7 +150,7 @@ public final class ExportCSVAction extends AbstractAction { writer.execute(); } } catch (NoCurrentCaseException ex) { - JOptionPane.showMessageDialog(component, Bundle.ExportCSV_actionPerformed_noCurrentCase()); + JOptionPane.showMessageDialog(component, Bundle.ExportCSV_saveNodesToCSV_noCurrentCase()); logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS } } @@ -165,13 +173,10 @@ public final class ExportCSVAction extends AbstractAction { String parentName = prop.getValue().toString(); // Strip off the count (if present) - System.out.println("parentName (raw) : " + parentName); parentName = parentName.replaceAll("\\([0-9]+\\)$", ""); - System.out.println("parentName (after paren regex) : " + parentName); // Strip out any invalid characters parentName = parentName.replaceAll("[\\\\/:*?\"<>|]", "_"); - System.out.println("parentName (after char regex) : " + parentName); return parentName + " " + dateStr; } catch (IllegalAccessException | InvocationTargetException ex) { @@ -190,7 +195,7 @@ public final class ExportCSVAction extends AbstractAction { * * @return The export directory path. */ - private static String getExportDirectory(Case openCase) { // TODO sync + private static String getExportDirectory(Case openCase) { String caseExportPath = openCase.getExportDirectory(); if (userDefinedExportPath == null) { @@ -306,6 +311,13 @@ public final class ExportCSVAction extends AbstractAction { return null; } + /** + * Convert list of values to a comma separated string. + * + * @param values Values to convert + * + * @return values as CSV + */ private String listToCSV(List values) { return "\"" + String.join("\",\"", values) + "\"\n"; } From 07ec6bdc3e9fa49092bfc1b0a26dd77ff16769a8 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Thu, 6 Jun 2019 11:09:28 -0400 Subject: [PATCH 052/106] Disable Interesting File buttons when no set is selected --- .../modules/interestingitems/FilesSetDefsPanel.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java index e919bf189f..d226e6765b 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java @@ -278,6 +278,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp //enable the new button FilesSetDefsPanel.this.newSetButton.setEnabled(canBeEnabled); FilesSetDefsPanel.this.importSetButton.setEnabled(canBeEnabled); + // Get the selected interesting files set and populate the set // components. FilesSet selectedSet = FilesSetDefsPanel.this.setsList.getSelectedValue(); @@ -302,6 +303,12 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp if (!FilesSetDefsPanel.this.rulesListModel.isEmpty()) { FilesSetDefsPanel.this.rulesList.setSelectedIndex(0); } + } else { + // Disable the edit, delete, copy, and export buttons + FilesSetDefsPanel.this.editSetButton.setEnabled(false); + FilesSetDefsPanel.this.deleteSetButton.setEnabled(false); + FilesSetDefsPanel.this.copySetButton.setEnabled(false); + FilesSetDefsPanel.this.exportSetButton.setEnabled(false); } } From f07c60c21582321c4e77e8ceb21dc8a089985580 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 6 Jun 2019 11:31:55 -0400 Subject: [PATCH 053/106] Fixed codacy issues and postgres problem --- .../casemodule/services/TagsManager.java | 12 +- .../ContentViewerTagManager.java | 122 ++++++++++----- .../contentviewers/MediaViewImagePanel.java | 142 +++++++++--------- .../contentviewers/imagetagging/ImageTag.java | 32 ++-- .../imagetagging/ImageTagCreator.java | 8 +- .../imagetagging/ImageTagsGroup.java | 4 +- .../imagetagging/ImageTagsUtility.java | 6 + 7 files changed, 195 insertions(+), 131 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java index e328b572de..a86d2bdb81 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java @@ -42,6 +42,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.datamodel.TskData.DbType; /** * A per case Autopsy service that manages the addition of content and artifact @@ -60,13 +61,16 @@ public class TagsManager implements Closeable { Case currentCase = (Case) evt.getNewValue(); try { CaseDbAccessManager caseDb = currentCase.getSleuthkitCase().getCaseDbAccessManager(); - //Create our custom application tags table, if need be. if (!caseDb.tableExists(ContentViewerTagManager.TABLE_NAME)) { - caseDb.createTable(ContentViewerTagManager.TABLE_NAME, ContentViewerTagManager.TABLE_SCHEMA); + if (currentCase.getSleuthkitCase().getDatabaseType().equals(DbType.SQLITE)) { + caseDb.createTable(ContentViewerTagManager.TABLE_NAME, ContentViewerTagManager.TABLE_SCHEMA_SQLITE); + } else if (currentCase.getSleuthkitCase().getDatabaseType().equals(DbType.POSTGRESQL)) { + caseDb.createTable(ContentViewerTagManager.TABLE_NAME, ContentViewerTagManager.TABLE_SCHEMA_POSTGRES); + } } } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, - String.format("Unable to create the %s table for image tag storage.", + LOGGER.log(Level.SEVERE, + String.format("Unable to create the %s table for image tag storage.", ContentViewerTagManager.TABLE_NAME), ex); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java index 33d7987d8a..d97bae73dc 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java @@ -23,7 +23,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.ContentTag; @@ -32,7 +31,8 @@ import org.sleuthkit.datamodel.TskCoreException; /** * A per case Autopsy service that manages the addition of content viewer tags * to the case database. This manager is also responsible for serializing and - * deserializing instances of your tag data objects for persistence and retrieval. + * deserializing instances of your tag data objects for persistence and + * retrieval. */ public class ContentViewerTagManager { @@ -41,7 +41,10 @@ public class ContentViewerTagManager { private static final ObjectMapper SERIALIZER = new ObjectMapper(); public static final String TABLE_NAME = "beta_tag_app_data"; - public static final String TABLE_SCHEMA = "(app_data_id INTEGER PRIMARY KEY, " + public static final String TABLE_SCHEMA_SQLITE = "(app_data_id INTEGER PRIMARY KEY, " + + "content_tag_id INTEGER NOT NULL, app_data TEXT NOT NULL, " + + "FOREIGN KEY(content_tag_id) REFERENCES content_tags(tag_id))"; + public static final String TABLE_SCHEMA_POSTGRES = "(app_data_id BIGSERIAL PRIMARY KEY, " + "content_tag_id INTEGER NOT NULL, app_data TEXT NOT NULL, " + "FOREIGN KEY(content_tag_id) REFERENCES content_tags(tag_id))"; @@ -55,8 +58,8 @@ public class ContentViewerTagManager { * generic tag data instance T will be automatically serialized into a * storable format. * - * @param Generic class type that will be serialized into a - * storable format for persistence. + * @param Generic class type that will be serialized into a storable + * format for persistence. * @param contentTag ContentTag that this ContentViewerTag is associated * with (1:1). * @param tagDataBean Data instance that contains the tag information to be @@ -64,20 +67,24 @@ public class ContentViewerTagManager { * @return An instance of a ContentViewerTag of type T, which contains all * the stored information. * - * @throws SerializationException Thrown if the tag data instance T - * could not be serialized into a storable format. + * @throws SerializationException Thrown if the tag data instance T could + * not be serialized into a storable format. * @throws TskCoreException Thrown if this operation did not successfully * persist in the case database. * @throws NoCurrentCaseException Thrown if invocation of this method occurs * when no case is open. */ - public static ContentViewerTag saveTag(ContentTag contentTag, T tagDataBean) throws SerializationException, TskCoreException, NoCurrentCaseException { + public static ContentViewerTag saveTag(ContentTag contentTag, T tagDataBean) + throws SerializationException, TskCoreException, NoCurrentCaseException { try { long contentTagId = contentTag.getId(); String serialAppData = SERIALIZER.writeValueAsString(tagDataBean); String insertTemplateInstance = String.format(INSERT_TAG_DATA, contentTagId, serialAppData); - long insertId = Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().insert(TABLE_NAME, insertTemplateInstance); + long insertId = Case.getCurrentCaseThrows() + .getSleuthkitCase() + .getCaseDbAccessManager() + .insert(TABLE_NAME, insertTemplateInstance); return new ContentViewerTag<>(insertId, contentTag, tagDataBean); } catch (JsonProcessingException ex) { throw new SerializationException("Unable to convert object instance into a storable format", ex); @@ -88,14 +95,14 @@ public class ContentViewerTagManager { * Updates the ContentViewerTag instance with the new tag data T and * persists the changes to the case database. * - * @param Generic class type that will be serialized into a - * storable format. + * @param Generic class type that will be serialized into a storable + * format. * @param oldTag ContentViewerTag instance to be updated * @param tagDataBean Data instance that contains the updated information to * be persisted. * - * @throws SerializationException Thrown if the tag data instance T - * could not be serialized into a storable format. + * @throws SerializationException Thrown if the tag data instance T could + * not be serialized into a storable format. * @throws TskCoreException Thrown if this operation did not successfully * persist in the case database. * @throws NoCurrentCaseException Thrown if invocation of this method occurs @@ -107,7 +114,9 @@ public class ContentViewerTagManager { String serialAppData = SERIALIZER.writeValueAsString(tagDataBean); String updateTemplateInstance = String.format(UPDATE_TAG_DATA, oldTag.getContentTag().getId(), serialAppData, oldTag.getId()); - Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager() + Case.getCurrentCaseThrows() + .getSleuthkitCase() + .getCaseDbAccessManager() .update(TABLE_NAME, updateTemplateInstance); return new ContentViewerTag<>(oldTag.getId(), oldTag.getContentTag(), tagDataBean); } catch (JsonProcessingException ex) { @@ -137,31 +146,66 @@ public class ContentViewerTagManager { * when no case is open. */ public static ContentViewerTag getTag(ContentTag contentTag, Class clazz) throws TskCoreException, NoCurrentCaseException { - try { - String selectTemplateInstance = String.format(SELECT_TAG_DATA, contentTag.getId()); - final ArrayList> result = new ArrayList<>(); - Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager() - .select(selectTemplateInstance, (ResultSet rs) -> { - try { - if (rs.next()) { - long tagId = rs.getLong(1); - String appDetails = rs.getString(3); - try { - T instance = SERIALIZER.readValue(appDetails, clazz); - result.add(new ContentViewerTag<>(tagId, contentTag, instance)); - } catch (IOException ex) { - //Databind for type T failed. Not a system error - //but rather a logic error on the part of the caller. - result.add(null); - } + String selectTemplateInstance = String.format(SELECT_TAG_DATA, contentTag.getId()); + final ResultWrapper> result = new ResultWrapper<>(); + Case.getCurrentCaseThrows() + .getSleuthkitCase() + .getCaseDbAccessManager() + .select(selectTemplateInstance, (ResultSet rs) -> { + try { + if (rs.next()) { + long tagId = rs.getLong(1); + String appDetails = rs.getString(3); + try { + T instance = SERIALIZER.readValue(appDetails, clazz); + result.setResult(new ContentViewerTag<>(tagId, contentTag, instance)); + } catch (IOException ex) { + //Databind for type T failed. Not a system error + //but rather a logic error on the part of the caller. + result.setResult(null); } - } catch (SQLException ex) { - throw new RuntimeException(ex); } - }); - return result.get(0); - } catch (RuntimeException ex) { - throw new TskCoreException("Unable to select tags from db", (Exception) ex.getCause()); + } catch (SQLException ex) { + result.setException(ex); + } + }); + + if (result.hasException()) { + throw new TskCoreException("Unable to select tag from case db", result.getException()); + } + + return result.getResult(); + } + + /** + * Wrapper for holding state in the CaseDbAccessQueryCallback. + * CaseDbAccessQueryCallback has no support for exception handling. + * + * @param + */ + private static class ResultWrapper { + + private T result; + private SQLException ex = null; + + public void setResult(T result) { + this.result = result; + } + + public void setException(SQLException ex) { + this.ex = ex; + } + + public boolean hasException() { + return this.ex != null; + } + + public SQLException getException() { + return ex; + } + + public T getResult() { + return result; } } @@ -176,7 +220,9 @@ public class ContentViewerTagManager { */ public static void deleteTag(ContentViewerTag contentViewerTag) throws TskCoreException, NoCurrentCaseException { String deleteTemplateInstance = String.format(DELETE_TAG_DATA, contentViewerTag.getId()); - Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager() + Case.getCurrentCaseThrows() + .getSleuthkitCase() + .getCaseDbAccessManager() .delete(TABLE_NAME, deleteTemplateInstance); } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index b4d1202f15..d9590059ea 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.contentviewers; +import com.sun.glass.events.KeyEvent; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; @@ -64,6 +65,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JSeparator; +import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.apache.commons.io.FilenameUtils; @@ -205,68 +207,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan } }); - //Respond to state events by enabling/disabling the correct - //buttons. - pcs.addPropertyChangeListener((event) -> { - State currentState = (State) event.getNewValue(); - switch (currentState) { - case CREATE: - createTagMenuItem.setEnabled(true); - deleteTagMenuItem.setEnabled(false); - hideTagsMenuItem.setEnabled(true); - exportTagsMenuItem.setEnabled(true); - break; - case SELECTED: - if (masterGroup.getChildren().contains(imageTagCreator)) { - imageTagCreator.disconnect(); - masterGroup.getChildren().remove(imageTagCreator); - } - createTagMenuItem.setEnabled(false); - deleteTagMenuItem.setEnabled(true); - hideTagsMenuItem.setEnabled(true); - exportTagsMenuItem.setEnabled(true); - break; - case HIDDEN: - createTagMenuItem.setEnabled(false); - deleteTagMenuItem.setEnabled(false); - hideTagsMenuItem.setEnabled(true); - hideTagsMenuItem.setText(DisplayOptions.SHOW_TAGS.getName()); - exportTagsMenuItem.setEnabled(false); - break; - case VISIBLE: - createTagMenuItem.setEnabled(true); - deleteTagMenuItem.setEnabled(false); - hideTagsMenuItem.setEnabled(true); - hideTagsMenuItem.setText(DisplayOptions.HIDE_TAGS.getName()); - exportTagsMenuItem.setEnabled(true); - break; - case DEFAULT: - case EMPTY: - if (masterGroup.getChildren().contains(imageTagCreator)) { - imageTagCreator.disconnect(); - } - createTagMenuItem.setEnabled(true); - deleteTagMenuItem.setEnabled(false); - hideTagsMenuItem.setEnabled(false); - hideTagsMenuItem.setText(DisplayOptions.HIDE_TAGS.getName()); - exportTagsMenuItem.setEnabled(false); - break; - case NONEMPTY: - createTagMenuItem.setEnabled(true); - deleteTagMenuItem.setEnabled(false); - hideTagsMenuItem.setEnabled(true); - exportTagsMenuItem.setEnabled(true); - break; - case DISABLE: - createTagMenuItem.setEnabled(false); - deleteTagMenuItem.setEnabled(false); - hideTagsMenuItem.setEnabled(false); - exportTagsMenuItem.setEnabled(false); - break; - default: - break; - } - }); + subscribeTagMenuItemsToStateChanges(); masterGroup.getChildren().add(tagsGroup); @@ -276,13 +217,13 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan if (masterGroup.getChildren().contains(imageTagCreator)) { return; } - - if(tagsGroup.getChildren().isEmpty()) { + + if (tagsGroup.getChildren().isEmpty()) { pcs.firePropertyChange(new PropertyChangeEvent(this, - "state", null, State.EMPTY)); + "state", null, State.EMPTY)); } else { pcs.firePropertyChange(new PropertyChangeEvent(this, - "state", null, State.CREATE)); + "state", null, State.CREATE)); } } else if (event.getPropertyName().equals(ImageTagControls.FOCUSED.getName())) { pcs.firePropertyChange(new PropertyChangeEvent(this, @@ -311,6 +252,74 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan } } + /** + * Handle tags menu item enabling and disabling given the state of the + * content viewer. For example, when the tags group is empty (no tags on image), + * disable delete menu item, hide menu item, and export menu item. + */ + private void subscribeTagMenuItemsToStateChanges() { + pcs.addPropertyChangeListener((event) -> { + State currentState = (State) event.getNewValue(); + switch (currentState) { + case CREATE: + createTagMenuItem.setEnabled(true); + deleteTagMenuItem.setEnabled(false); + hideTagsMenuItem.setEnabled(true); + exportTagsMenuItem.setEnabled(true); + break; + case SELECTED: + if (masterGroup.getChildren().contains(imageTagCreator)) { + imageTagCreator.disconnect(); + masterGroup.getChildren().remove(imageTagCreator); + } + createTagMenuItem.setEnabled(false); + deleteTagMenuItem.setEnabled(true); + hideTagsMenuItem.setEnabled(true); + exportTagsMenuItem.setEnabled(true); + break; + case HIDDEN: + createTagMenuItem.setEnabled(false); + deleteTagMenuItem.setEnabled(false); + hideTagsMenuItem.setEnabled(true); + hideTagsMenuItem.setText(DisplayOptions.SHOW_TAGS.getName()); + exportTagsMenuItem.setEnabled(false); + break; + case VISIBLE: + createTagMenuItem.setEnabled(true); + deleteTagMenuItem.setEnabled(false); + hideTagsMenuItem.setEnabled(true); + hideTagsMenuItem.setText(DisplayOptions.HIDE_TAGS.getName()); + exportTagsMenuItem.setEnabled(true); + break; + case DEFAULT: + case EMPTY: + if (masterGroup.getChildren().contains(imageTagCreator)) { + imageTagCreator.disconnect(); + } + createTagMenuItem.setEnabled(true); + deleteTagMenuItem.setEnabled(false); + hideTagsMenuItem.setEnabled(false); + hideTagsMenuItem.setText(DisplayOptions.HIDE_TAGS.getName()); + exportTagsMenuItem.setEnabled(false); + break; + case NONEMPTY: + createTagMenuItem.setEnabled(true); + deleteTagMenuItem.setEnabled(false); + hideTagsMenuItem.setEnabled(true); + exportTagsMenuItem.setEnabled(true); + break; + case DISABLE: + createTagMenuItem.setEnabled(false); + deleteTagMenuItem.setEnabled(false); + hideTagsMenuItem.setEnabled(false); + exportTagsMenuItem.setEnabled(false); + break; + default: + break; + } + }); + } + public boolean isInited() { return fxInited; } @@ -710,7 +719,6 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private void deleteTag() { Platform.runLater(() -> { ImageTag tagInFocus = tagsGroup.getFocus(); - //Null should not be expected, but just as a safetly precaution if (tagInFocus == null) { return; } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java index 80b1cbe07f..765d98c4b2 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java @@ -81,35 +81,35 @@ public final class ImageTag extends Group { EditHandle bottomLeft = new EditHandle(physicalTag) .setPosition(Position.bottom(), Position.left()) - .setDrag(dragBoundary, Drag.bottom(), Drag.left()); + .setDrag(dragBoundary, Draggable.bottom(), Draggable.left()); EditHandle bottomRight = new EditHandle(physicalTag) .setPosition(Position.bottom(), Position.right()) - .setDrag(dragBoundary, Drag.bottom(), Drag.right()); + .setDrag(dragBoundary, Draggable.bottom(), Draggable.right()); EditHandle topLeft = new EditHandle(physicalTag) .setPosition(Position.top(), Position.left()) - .setDrag(dragBoundary, Drag.top(), Drag.left()); + .setDrag(dragBoundary, Draggable.top(), Draggable.left()); EditHandle topRight = new EditHandle(physicalTag) .setPosition(Position.top(), Position.right()) - .setDrag(dragBoundary, Drag.top(), Drag.right()); + .setDrag(dragBoundary, Draggable.top(), Draggable.right()); EditHandle bottomMiddle = new EditHandle(physicalTag) .setPosition(Position.bottom(), Position.xMiddle()) - .setDrag(dragBoundary, Drag.bottom()); + .setDrag(dragBoundary, Draggable.bottom()); EditHandle topMiddle = new EditHandle(physicalTag) .setPosition(Position.top(), Position.xMiddle()) - .setDrag(dragBoundary, Drag.top()); + .setDrag(dragBoundary, Draggable.top()); EditHandle rightMiddle = new EditHandle(physicalTag) .setPosition(Position.right(), Position.yMiddle()) - .setDrag(dragBoundary, Drag.right()); + .setDrag(dragBoundary, Draggable.right()); EditHandle leftMiddle = new EditHandle(physicalTag) .setPosition(Position.left(), Position.yMiddle()) - .setDrag(dragBoundary, Drag.left()); + .setDrag(dragBoundary, Draggable.left()); //The "logical" tag is the Group this.getChildren().addAll(physicalTag, bottomLeft, bottomRight, topLeft, @@ -133,7 +133,7 @@ public final class ImageTag extends Group { } /** - * Get the app tag that this class represents. + * Get the content viewer tag that this class represents. * * @return */ @@ -226,9 +226,9 @@ public final class ImageTag extends Group { * @param vals * @return */ - public EditHandle setDrag(Boundary bounds, Drag... vals) { + public EditHandle setDrag(Boundary bounds, Draggable... vals) { this.setOnMouseDragged((event) -> { - for (Drag drag : vals) { + for (Draggable drag : vals) { drag.perform(parent, event, bounds); } }); @@ -282,11 +282,11 @@ public final class ImageTag extends Group { * Drag strategies for manipulating the physical tag from a given side of * the rectangle. */ - static interface Drag { + static interface Draggable { void perform(PhysicalTag parent, MouseEvent event, Boundary b); - static Drag bottom() { + static Draggable bottom() { return (parent, event, bounds) -> { if (!bounds.isPointInBounds(event.getX(), event.getY())) { return; @@ -299,7 +299,7 @@ public final class ImageTag extends Group { }; } - static Drag top() { + static Draggable top() { return (parent, event, bounds) -> { if (!bounds.isPointInBounds(event.getX(), event.getY())) { return; @@ -313,7 +313,7 @@ public final class ImageTag extends Group { }; } - static Drag left() { + static Draggable left() { return (parent, event, bounds) -> { if (!bounds.isPointInBounds(event.getX(), event.getY())) { return; @@ -327,7 +327,7 @@ public final class ImageTag extends Group { }; } - static Drag right() { + static Draggable right() { return (parent, event, bounds) -> { if (!bounds.isPointInBounds(event.getX(), event.getY())) { return; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java index 4df4328b2d..b2500baa24 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java @@ -41,7 +41,7 @@ public final class ImageTagCreator extends Rectangle { //Rectangle lines should be 1.5% of the image. This level of thickness has //a good balance between visual acuity and loss of selection at the borders //of the image. - private double lineThicknessAsPercent = 1.5; + private final double lineThicknessAsPercent = 1.5; private final double minArea; //Used to update listeners of the new tag boundaries @@ -53,7 +53,7 @@ public final class ImageTagCreator extends Rectangle { //Handles the unregistering this ImageTagCreator from mouse press, mouse drag, //and mouse release events of the source image. - private final Runnable disconnect; + private final Runnable disconnectRunnable; /** * Adds tagging support to an image, where the 'tag' rectangle will be the @@ -143,7 +143,7 @@ public final class ImageTagCreator extends Rectangle { image.addEventHandler(MouseEvent.MOUSE_RELEASED, this.mouseReleased); //Used to remove itself from mouse events on the source image - disconnect = () -> { + disconnectRunnable = () -> { defaultSettings(); image.removeEventHandler(MouseEvent.MOUSE_RELEASED, mouseReleased); image.removeEventHandler(MouseEvent.MOUSE_DRAGGED, mouseDragged); @@ -166,7 +166,7 @@ public final class ImageTagCreator extends Rectangle { * Removes itself from mouse events on the source image. */ public void disconnect() { - this.disconnect.run(); + this.disconnectRunnable.run(); } /** diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java index fcbbb145bd..a9f23304a3 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsGroup.java @@ -119,9 +119,9 @@ public final class ImageTagsGroup extends Group { * @param n */ private void requestFocus(ImageTag n) { - if (currentFocus == n) { + if (n.equals(currentFocus)) { return; - } else if (currentFocus != null && currentFocus != n) { + } else if (currentFocus != null && !currentFocus.equals(n)) { resetFocus(currentFocus); } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java index 67ec058c84..36982afec1 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java @@ -28,6 +28,9 @@ import org.opencv.highgui.Highgui; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; +/** + * Utility class for handling content viewer tags on images. + */ public class ImageTagsUtility { /** @@ -63,4 +66,7 @@ public class ImageTagsUtility { return output; } + + private ImageTagsUtility(){ + } } From d7daf2b38370899cb2d35d1a14053c54932e9744 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 6 Jun 2019 11:48:50 -0400 Subject: [PATCH 054/106] Removed unused imports --- .../sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index d9590059ea..f4af5752b9 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.contentviewers; -import com.sun.glass.events.KeyEvent; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; @@ -65,7 +64,6 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JSeparator; -import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.apache.commons.io.FilenameUtils; From e2ded8a6454f0dfc9f613be9396c4ee153a069af Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 6 Jun 2019 11:56:54 -0400 Subject: [PATCH 055/106] Fixed new round of codacy issues and renamed package to reflect naming conventions --- .../autopsy/casemodule/services/TagsManager.java | 16 +++++++++------- .../ContentViewerTagManager.java | 4 ++-- .../contentviewers/MediaViewImagePanel.java | 6 +++--- .../contentviewers/imagetagging/ImageTag.java | 2 +- .../imagetagging/ImageTagCreator.java | 2 +- .../imagetagging/ImageTagsUtility.java | 2 +- 6 files changed, 17 insertions(+), 15 deletions(-) rename Core/src/org/sleuthkit/autopsy/casemodule/services/{applicationtags => contentviewertags}/ContentViewerTagManager.java (98%) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java index a86d2bdb81..40597e9af3 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java @@ -31,7 +31,7 @@ import java.util.logging.Level; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager; +import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifactTag; @@ -61,12 +61,14 @@ public class TagsManager implements Closeable { Case currentCase = (Case) evt.getNewValue(); try { CaseDbAccessManager caseDb = currentCase.getSleuthkitCase().getCaseDbAccessManager(); - if (!caseDb.tableExists(ContentViewerTagManager.TABLE_NAME)) { - if (currentCase.getSleuthkitCase().getDatabaseType().equals(DbType.SQLITE)) { - caseDb.createTable(ContentViewerTagManager.TABLE_NAME, ContentViewerTagManager.TABLE_SCHEMA_SQLITE); - } else if (currentCase.getSleuthkitCase().getDatabaseType().equals(DbType.POSTGRESQL)) { - caseDb.createTable(ContentViewerTagManager.TABLE_NAME, ContentViewerTagManager.TABLE_SCHEMA_POSTGRES); - } + if (caseDb.tableExists(ContentViewerTagManager.TABLE_NAME)) { + return; + } + + if (currentCase.getSleuthkitCase().getDatabaseType().equals(DbType.SQLITE)) { + caseDb.createTable(ContentViewerTagManager.TABLE_NAME, ContentViewerTagManager.TABLE_SCHEMA_SQLITE); + } else if (currentCase.getSleuthkitCase().getDatabaseType().equals(DbType.POSTGRESQL)) { + caseDb.createTable(ContentViewerTagManager.TABLE_NAME, ContentViewerTagManager.TABLE_SCHEMA_POSTGRESQL); } } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/contentviewertags/ContentViewerTagManager.java similarity index 98% rename from Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java rename to Core/src/org/sleuthkit/autopsy/casemodule/services/contentviewertags/ContentViewerTagManager.java index d97bae73dc..8d117e908b 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/applicationtags/ContentViewerTagManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/contentviewertags/ContentViewerTagManager.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.casemodule.services.applicationtags; +package org.sleuthkit.autopsy.casemodule.services.contentviewertags; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -44,7 +44,7 @@ public class ContentViewerTagManager { public static final String TABLE_SCHEMA_SQLITE = "(app_data_id INTEGER PRIMARY KEY, " + "content_tag_id INTEGER NOT NULL, app_data TEXT NOT NULL, " + "FOREIGN KEY(content_tag_id) REFERENCES content_tags(tag_id))"; - public static final String TABLE_SCHEMA_POSTGRES = "(app_data_id BIGSERIAL PRIMARY KEY, " + public static final String TABLE_SCHEMA_POSTGRESQL = "(app_data_id BIGSERIAL PRIMARY KEY, " + "content_tag_id INTEGER NOT NULL, app_data TEXT NOT NULL, " + "FOREIGN KEY(content_tag_id) REFERENCES content_tags(tag_id))"; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index f4af5752b9..3f33d8ff47 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -74,9 +74,9 @@ import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog; import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog.TagNameAndComment; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager; -import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager.ContentViewerTag; -import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager.SerializationException; +import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager; +import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag; +import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.SerializationException; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsUtility; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagControls; import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java index 765d98c4b2..10708f9d1a 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTag.java @@ -32,7 +32,7 @@ import javafx.scene.input.MouseEvent; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.shape.Rectangle; -import org.sleuthkit.autopsy.casemodule.services.applicationtags.ContentViewerTagManager.ContentViewerTag; +import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag; /** * A tagged region displayed over an image. This class contains a "physical tag" diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java index b2500baa24..885dd6c18f 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java @@ -41,7 +41,7 @@ public final class ImageTagCreator extends Rectangle { //Rectangle lines should be 1.5% of the image. This level of thickness has //a good balance between visual acuity and loss of selection at the borders //of the image. - private final double lineThicknessAsPercent = 1.5; + private final static double lineThicknessAsPercent = 1.5; private final double minArea; //Used to update listeners of the new tag boundaries diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java index 36982afec1..1d1d164e2f 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java @@ -31,7 +31,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Utility class for handling content viewer tags on images. */ -public class ImageTagsUtility { +public final class ImageTagsUtility { /** * Embeds the tag regions into an image (represented as an AbstractFile). From 65bf9cc4970a9323f5a822496e970c57c432797d Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 6 Jun 2019 12:52:09 -0400 Subject: [PATCH 056/106] 5061 log unlogged exceptions, fix copy paste and typo comment errors --- .../translators/BingTranslator.java | 2 +- .../translators/BingTranslatorSettings.java | 4 ++-- .../translators/BingTranslatorSettingsPanel.java | 16 +++++++--------- .../translators/GoogleTranslator.java | 2 ++ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java index 4c2526ab9b..2b234f71d3 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java @@ -71,7 +71,7 @@ public class BingTranslator implements TextTranslator { } /** - * Converts an input test to the JSON format required by Bing Translator, + * Converts an input text to the JSON format required by Bing Translator, * posts it to Microsoft, and returns the JSON text response. * * @param string The input text to be translated. diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java index dcd54fd6f9..d00ed61f77 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java @@ -22,7 +22,7 @@ import org.apache.commons.lang3.StringUtils; import org.sleuthkit.autopsy.coreutils.ModuleSettings; /** - * Class to handle the settings associated with the GoogleTranslator + * Class to handle the settings associated with the BingTranslator */ public final class BingTranslatorSettings { @@ -35,7 +35,7 @@ public final class BingTranslatorSettings { private String targetLanguageCode; /** - * Construct a new GoogleTranslatorSettingsObject + * Construct a new BingTranslatorSettings object */ BingTranslatorSettings() { loadSettings(); diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java index cb115354e2..9df244d034 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java @@ -36,7 +36,7 @@ import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle.Messages; /** - * Settings panel for the GoogleTranslator + * Settings panel for the BingTranslator */ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { @@ -47,7 +47,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { private String targetLanguageCode = ""; /** - * Creates new form GoogleTranslatorSettingsPanel + * Creates new form BingTranslatorSettingsPanel */ public BingTranslatorSettingsPanel(String authenticationKey, String code) { initComponents(); @@ -254,16 +254,13 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { private javax.swing.JLabel untranslatedLabel; private javax.swing.JLabel warningLabel; // End of variables declaration//GEN-END:variables + /** - * Converts an input test to the JSON format required by Bing Translator, - * posts it to Microsoft, and returns the JSON text response. + * Attempts to translate the text specified in the Untranslated field using + * the settings currently specified but not necessarily saved * - * @param string The input text to be translated. + * @return true if the translation was able to be performed, false otherwise * - * @return The translation response as a JSON string - * - * @throws IOException if the request could not be executed due to - * cancellation, a connectivity problem or timeout. */ private boolean testTranslationSetup() { testResultValueLabel.setText(""); @@ -291,6 +288,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { testResultValueLabel.setText(translation0.get("text").getAsString()); return true; } catch (IOException | IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException e) { + logger.log(Level.WARNING, "Test of Bing Translator failed due to exception", ex); return false; } } diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java index de61222072..46bdd6da67 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java @@ -73,8 +73,10 @@ public final class GoogleTranslator implements TextTranslator { address = InetAddress.getByName(host); return address.isReachable(1500); } catch (UnknownHostException ex) { + logger.log(Level.WARNING, "Unable to reach google.com due to unknown host", ex); return false; } catch (IOException ex) { + logger.log(Level.WARNING, "Unable to reach google.com due IOException", ex); return false; } } From 3610495657754e2850886dd19a09d673f5aeef0f Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 6 Jun 2019 12:54:50 -0400 Subject: [PATCH 057/106] 5061 fix typo in exception variable name --- .../translators/BingTranslatorSettingsPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java index 9df244d034..1be932873c 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java @@ -288,7 +288,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { testResultValueLabel.setText(translation0.get("text").getAsString()); return true; } catch (IOException | IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException e) { - logger.log(Level.WARNING, "Test of Bing Translator failed due to exception", ex); + logger.log(Level.WARNING, "Test of Bing Translator failed due to exception", e); return false; } } From 2823ef0396924550e9e0a33c9501fb1dd36c54f5 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 6 Jun 2019 13:55:07 -0400 Subject: [PATCH 058/106] Disabled 'Text' content viewer tab for diectories and empty files --- .../textcontentviewer/TextContentViewer.java | 12 ++++++++++++ .../textcontentviewer/TextContentViewerPanel.java | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/TextContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/TextContentViewer.java index 81384acbaa..d7f853caab 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/TextContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/TextContentViewer.java @@ -23,6 +23,7 @@ import org.openide.nodes.Node; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; +import org.sleuthkit.datamodel.AbstractFile; /** * A DataContentViewer that displays text with the TextViewers available. @@ -90,6 +91,17 @@ public class TextContentViewer implements DataContentViewer { if (node == null) { return false; } + // get the node's File, if it has one + AbstractFile file = node.getLookup().lookup(AbstractFile.class); + if (file == null) { + return false; + } + + // disable the text content viewer for directories and empty files + if (file.isDir() || file.getSize() == 0) { + return false; + } + return panel.isSupported(node); } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/TextContentViewerPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/TextContentViewerPanel.java index e1af060cfc..7c80d606fd 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/TextContentViewerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/TextContentViewerPanel.java @@ -79,7 +79,7 @@ public class TextContentViewerPanel extends javax.swing.JPanel implements DataCo } /** - * Deterime wether the content viewer which displays this panel isSupported. + * Determine whether the content viewer which displays this panel isSupported. * This panel is supported if any of the TextViewer's displayed in it are * supported. * From ae0aeddc5fa73266597cca05d540bf0740c7f400 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Thu, 6 Jun 2019 14:07:33 -0400 Subject: [PATCH 059/106] cleanup --- .../org/sleuthkit/autopsy/directorytree/ExportCSVAction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java index 7fa4560afc..4a87f72828 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java @@ -60,7 +60,7 @@ public final class ExportCSVAction extends AbstractAction { private final static String DEFAULT_FILENAME = "Results"; private final static List columnsToSkip = Arrays.asList("S", "C", "O"); - private static volatile String userDefinedExportPath; // TODO make volatile or whatever + private static String userDefinedExportPath; // This class is a singleton to support multi-selection of nodes, since // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every @@ -219,7 +219,7 @@ public final class ExportCSVAction extends AbstractAction { * @param exportPath The export path. * @param openCase The current case. */ - private static void updateExportDirectory(String exportPath, Case openCase) { // TODO sync + private static void updateExportDirectory(String exportPath, Case openCase) { if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) { userDefinedExportPath = null; } else { From 1285e914f24d6b13bdf81f072185009546de8f90 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 6 Jun 2019 14:28:11 -0400 Subject: [PATCH 060/106] 5129 add new labels for instructions --- .../BingTranslatorSettingsPanel.form | 93 +++++++++++++------ .../BingTranslatorSettingsPanel.java | 64 ++++++++----- .../translators/Bundle.properties | 2 + .../translators/Bundle.properties-MERGED | 1 + .../GoogleTranslatorSettingsPanel.form | 60 ++++++++++-- .../GoogleTranslatorSettingsPanel.java | 44 ++++++++- .../texttranslation/ui/Bundle.properties | 1 + .../ui/Bundle.properties-MERGED | 1 + .../ui/TranslationOptionsPanel.form | 24 +++-- .../ui/TranslationOptionsPanel.java | 13 ++- 10 files changed, 233 insertions(+), 70 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form index cd855ac419..44ca6e1c39 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form @@ -16,50 +16,54 @@ - - + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - + + - + + + @@ -79,7 +83,7 @@ - + @@ -168,5 +172,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java index 1be932873c..9cc6ae560c 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java @@ -135,6 +135,8 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { resultLabel = new javax.swing.JLabel(); testResultValueLabel = new javax.swing.JLabel(); authenticationKeyLabel = new javax.swing.JLabel(); + instructionsScrollPane = new javax.swing.JScrollPane(); + instructionsTextArea = new javax.swing.JTextArea(); authenticationKeyField.setToolTipText(org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.authenticationKeyField.toolTipText")); // NOI18N @@ -167,6 +169,19 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(authenticationKeyLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.authenticationKeyLabel.text")); // NOI18N + instructionsScrollPane.setBorder(javax.swing.BorderFactory.createEtchedBorder()); + instructionsScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + + instructionsTextArea.setEditable(false); + instructionsTextArea.setBackground(new java.awt.Color(240, 240, 240)); + instructionsTextArea.setColumns(20); + instructionsTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N + instructionsTextArea.setLineWrap(true); + instructionsTextArea.setRows(2); + instructionsTextArea.setText(org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.instructionsTextArea.text")); // NOI18N + instructionsTextArea.setWrapStyleWord(true); + instructionsScrollPane.setViewportView(instructionsTextArea); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -174,37 +189,40 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(instructionsScrollPane) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(layout.createSequentialGroup() - .addComponent(targetLanguageLabel) - .addGap(18, 18, 18) - .addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 192, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGap(0, 0, Short.MAX_VALUE)) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(authenticationKeyLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(authenticationKeyField, javax.swing.GroupLayout.PREFERRED_SIZE, 486, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 20, Short.MAX_VALUE)) + .addComponent(authenticationKeyField, javax.swing.GroupLayout.PREFERRED_SIZE, 486, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createSequentialGroup() - .addComponent(testButton, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(25, 25, 25) - .addComponent(untranslatedLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(resultLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(testResultValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) - .addContainerGap()))) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(targetLanguageLabel) + .addComponent(testButton, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(untranslatedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 66, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(resultLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(testResultValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addComponent(targetLanguageComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(276, 276, 276))))) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() + .addComponent(instructionsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(authenticationKeyField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(authenticationKeyLabel)) @@ -221,7 +239,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { .addComponent(testResultValueLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); }// //GEN-END:initComponents @@ -245,6 +263,8 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JTextField authenticationKeyField; private javax.swing.JLabel authenticationKeyLabel; + private javax.swing.JScrollPane instructionsScrollPane; + private javax.swing.JTextArea instructionsTextArea; private javax.swing.JLabel resultLabel; private javax.swing.JComboBox targetLanguageComboBox; private javax.swing.JLabel targetLanguageLabel; diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties index 0021118e14..9cc11dd938 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties @@ -13,3 +13,5 @@ GoogleTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: GoogleTranslatorSettingsPanel.resultLabel.text=Result: GoogleTranslatorSettingsPanel.testResultValueLabel.text= BingTranslatorSettingsPanel.authenticationKeyLabel.text=Authentication Key: +BingTranslatorSettingsPanel.instructionsTextArea.text=You will need to provide a Microsoft Translation authenitcation key for your Microsoft Translation account. Instruction on how to create one are available here: +GoogleTranslatorSettingsPanel.instructionsTextArea.text=You will need a JSON credentials file which contains your service account key for your Google Translate account. Information on how to create a service account key is available here: https://cloud.google.com/iam/docs/creating-managing-service-account-keys diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED index 51cbb9a26f..3cc13aa983 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED @@ -26,3 +26,4 @@ GoogleTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: GoogleTranslatorSettingsPanel.resultLabel.text=Result: GoogleTranslatorSettingsPanel.testResultValueLabel.text= BingTranslatorSettingsPanel.authenticationKeyLabel.text=Authentication Key: +GoogleTranslatorSettingsPanel.jTextArea1.text=You will need a JSON credentials file which contains your service account key for your Google Translate account. Information on how to create a service account key is available here: https://cloud.google.com/iam/docs/creating-managing-service-account-keys diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form index b55a40b30f..cf280d463c 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form @@ -16,9 +16,13 @@ - - + + + + + + @@ -27,7 +31,7 @@ - + @@ -38,7 +42,6 @@ - @@ -50,6 +53,10 @@ + + + + @@ -58,17 +65,19 @@ + + - + - + @@ -78,6 +87,7 @@ + @@ -94,6 +104,9 @@ + + +
@@ -175,5 +188,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java index 84fad9e8e1..23aaf0902a 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java @@ -196,10 +196,17 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { untranslatedLabel = new javax.swing.JLabel(); testUntranslatedTextField = new javax.swing.JTextField(); testButton = new javax.swing.JButton(); + instructionsScrollPane = new javax.swing.JScrollPane(); + instructionsTextArea = new javax.swing.JTextArea(); org.openide.awt.Mnemonics.setLocalizedText(credentialsLabel, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.credentialsLabel.text")); // NOI18N credentialsPathField.setEditable(false); + credentialsPathField.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + credentialsPathFieldActionPerformed(evt); + } + }); org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.browseButton.text")); // NOI18N browseButton.addActionListener(new java.awt.event.ActionListener() { @@ -234,6 +241,19 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { } }); + instructionsScrollPane.setBorder(javax.swing.BorderFactory.createEtchedBorder()); + instructionsScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + + instructionsTextArea.setEditable(false); + instructionsTextArea.setBackground(new java.awt.Color(240, 240, 240)); + instructionsTextArea.setColumns(20); + instructionsTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N + instructionsTextArea.setLineWrap(true); + instructionsTextArea.setRows(2); + instructionsTextArea.setText(org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.instructionsTextArea.text")); // NOI18N + instructionsTextArea.setWrapStyleWord(true); + instructionsScrollPane.setViewportView(instructionsTextArea); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -241,6 +261,9 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(instructionsScrollPane) + .addContainerGap()) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(credentialsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) @@ -248,14 +271,13 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(credentialsPathField, javax.swing.GroupLayout.DEFAULT_SIZE, 451, Short.MAX_VALUE) + .addComponent(credentialsPathField) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(browseButton) .addGap(14, 14, 14)) .addGroup(layout.createSequentialGroup() .addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 317, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, Short.MAX_VALUE)))) - .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createSequentialGroup() .addComponent(testButton, javax.swing.GroupLayout.PREFERRED_SIZE, 83, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) @@ -265,12 +287,17 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(resultLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(testResultValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addComponent(testResultValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() + .addComponent(instructionsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(credentialsLabel) .addComponent(credentialsPathField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -279,7 +306,7 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(targetLanguageLabel) .addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 15, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(testButton) .addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -287,7 +314,8 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { .addComponent(resultLabel) .addComponent(testResultValueLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); }// //GEN-END:initComponents @@ -327,10 +355,16 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { } }//GEN-LAST:event_testButtonActionPerformed + private void credentialsPathFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_credentialsPathFieldActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_credentialsPathFieldActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton browseButton; private javax.swing.JLabel credentialsLabel; private javax.swing.JTextField credentialsPathField; + private javax.swing.JScrollPane instructionsScrollPane; + private javax.swing.JTextArea instructionsTextArea; private javax.swing.JLabel resultLabel; private javax.swing.JComboBox targetLanguageComboBox; private javax.swing.JLabel targetLanguageLabel; diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties index 08c2523b0f..9f01ce498c 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties @@ -6,3 +6,4 @@ TranslationContentPanel.ocrLabel.text=OCR: TranslationOptionsPanel.translationServiceLabel.text=Text translator: TranslationOptionsPanelController.moduleErr=Module Error TranslationOptionsPanelController.moduleErr.msg=A module caused an error listening to TranslationSettingsPanelController updates. See log to determine which module. Some data could be incomplete. +TranslationOptionsPanel.translationOptionsDescription.text=Configure a 3rd party text translation service to enable text and file name translation. diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties-MERGED index 0304d7b1f3..00718e5aa1 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties-MERGED @@ -25,3 +25,4 @@ TranslationOptionsPanel.translationDisabled.text=Translation disabled TranslationOptionsPanel.translationServiceLabel.text=Text translator: TranslationOptionsPanelController.moduleErr=Module Error TranslationOptionsPanelController.moduleErr.msg=A module caused an error listening to TranslationSettingsPanelController updates. See log to determine which module. Some data could be incomplete. +TranslationOptionsPanel.translationOptionsDescription.text=Configure a 3rd party text translation service to enable text and file name translation. diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslationOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslationOptionsPanel.form index 20f5daa7f5..8705086cfe 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslationOptionsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslationOptionsPanel.form @@ -19,13 +19,14 @@ - + + - + - + - + @@ -34,14 +35,16 @@ - + + + - + - + @@ -66,5 +69,12 @@ + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslationOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslationOptionsPanel.java index 705ca45d84..b742f51be0 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslationOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslationOptionsPanel.java @@ -145,6 +145,7 @@ public class TranslationOptionsPanel extends javax.swing.JPanel { translatorComboBox = new javax.swing.JComboBox<>(); translationServiceLabel = new javax.swing.JLabel(); translationServicePanel = new javax.swing.JPanel(); + translationOptionsDescription = new javax.swing.JLabel(); translatorComboBox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -156,6 +157,8 @@ public class TranslationOptionsPanel extends javax.swing.JPanel { translationServicePanel.setLayout(new java.awt.BorderLayout()); + org.openide.awt.Mnemonics.setLocalizedText(translationOptionsDescription, org.openide.util.NbBundle.getMessage(TranslationOptionsPanel.class, "TranslationOptionsPanel.translationOptionsDescription.text")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -163,18 +166,21 @@ public class TranslationOptionsPanel extends javax.swing.JPanel { .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(translationServicePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addComponent(translationServiceLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGap(10, 10, 10) .addComponent(translatorComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 214, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 162, Short.MAX_VALUE)) - .addComponent(translationServicePanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(translationOptionsDescription, javax.swing.GroupLayout.DEFAULT_SIZE, 462, Short.MAX_VALUE)) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() + .addComponent(translationOptionsDescription, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(translatorComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(translationServiceLabel)) @@ -190,6 +196,7 @@ public class TranslationOptionsPanel extends javax.swing.JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel translationOptionsDescription; private javax.swing.JLabel translationServiceLabel; private javax.swing.JPanel translationServicePanel; private javax.swing.JComboBox translatorComboBox; From 6b5fc4af0e96cbdcae13821a58b6081c66fc0bd5 Mon Sep 17 00:00:00 2001 From: Raman Date: Thu, 6 Jun 2019 14:46:54 -0400 Subject: [PATCH 061/106] 5079: Ingest multiple vcards from a vcf file --- .../org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java index 15d52b536e..7e5a321313 100755 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java @@ -143,8 +143,9 @@ final class VcardParser { * @throws NoCurrentCaseException If there is no open case. */ void parse(File vcardFile, AbstractFile abstractFile) throws IOException, NoCurrentCaseException { - VCard vcard = Ezvcard.parse(vcardFile).first(); - addContactArtifact(vcard, abstractFile); + for (VCard vcard: Ezvcard.parse(vcardFile).all()) { + addContactArtifact(vcard, abstractFile); + } } From a7afef0e68af25ad6e78b0d697ed5841d9056340 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 6 Jun 2019 14:47:31 -0400 Subject: [PATCH 062/106] Fixed bug which was allocating too much memory to byte buffers --- .../autopsy/texttranslation/ui/TranslatedTextViewer.java | 6 +++--- 1 file changed, 3 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 e9656bb0a9..4b284c53fa 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslatedTextViewer.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslatedTextViewer.java @@ -296,8 +296,8 @@ public final class TranslatedTextViewer implements TextViewer { //Correct for UTF-8 byte[] resultInUTF8Bytes = result.getBytes("UTF8"); - byte[] trimTo1MB = Arrays.copyOfRange(resultInUTF8Bytes, 0, MAX_SIZE_1MB ); - return new String(trimTo1MB, "UTF-8"); + byte[] trimToArraySize = Arrays.copyOfRange(resultInUTF8Bytes, 0, Math.min(resultInUTF8Bytes.length, MAX_SIZE_1MB) ); + return new String(trimToArraySize, "UTF-8"); } /** @@ -343,7 +343,7 @@ public final class TranslatedTextViewer implements TextViewer { } // The trim is on here because HTML files were observed with nearly 1MB of white space at the end - return textBuilder.toString().trim(); + return textBuilder.toString(); } /** From d4514e7f3bd3f02d14ce2f072358009115329ddc Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 6 Jun 2019 15:04:49 -0400 Subject: [PATCH 063/106] 5129 reword translator instructions --- .../autopsy/texttranslation/translators/Bundle.properties | 2 +- .../texttranslation/translators/Bundle.properties-MERGED | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties index 9cc11dd938..b09c2ae682 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties @@ -13,5 +13,5 @@ GoogleTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: GoogleTranslatorSettingsPanel.resultLabel.text=Result: GoogleTranslatorSettingsPanel.testResultValueLabel.text= BingTranslatorSettingsPanel.authenticationKeyLabel.text=Authentication Key: -BingTranslatorSettingsPanel.instructionsTextArea.text=You will need to provide a Microsoft Translation authenitcation key for your Microsoft Translation account. Instruction on how to create one are available here: +BingTranslatorSettingsPanel.instructionsTextArea.text=You will need to provide a Microsoft Translator authentication key for your Microsoft Translator account. Instructions on how to get one are available here: https://docs.microsoft.com/en-us/azure/cognitive-services/translator/translator-text-how-to-signup GoogleTranslatorSettingsPanel.instructionsTextArea.text=You will need a JSON credentials file which contains your service account key for your Google Translate account. Information on how to create a service account key is available here: https://cloud.google.com/iam/docs/creating-managing-service-account-keys diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED index 3cc13aa983..4c32b7abab 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED @@ -26,4 +26,5 @@ GoogleTranslatorSettingsPanel.untranslatedLabel.text=Untranslated: GoogleTranslatorSettingsPanel.resultLabel.text=Result: GoogleTranslatorSettingsPanel.testResultValueLabel.text= BingTranslatorSettingsPanel.authenticationKeyLabel.text=Authentication Key: -GoogleTranslatorSettingsPanel.jTextArea1.text=You will need a JSON credentials file which contains your service account key for your Google Translate account. Information on how to create a service account key is available here: https://cloud.google.com/iam/docs/creating-managing-service-account-keys +BingTranslatorSettingsPanel.instructionsTextArea.text=You will need to provide a Microsoft Translator authentication key for your Microsoft Translator account. Instructions on how to get one are available here: https://docs.microsoft.com/en-us/azure/cognitive-services/translator/translator-text-how-to-signup +GoogleTranslatorSettingsPanel.instructionsTextArea.text=You will need a JSON credentials file which contains your service account key for your Google Translate account. Information on how to create a service account key is available here: https://cloud.google.com/iam/docs/creating-managing-service-account-keys From e837513c91a7d94ea4715b7c13fe641807ca6ffd Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Fri, 7 Jun 2019 10:09:06 -0400 Subject: [PATCH 064/106] Removed a comment --- .../autopsy/texttranslation/ui/TranslatedTextViewer.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslatedTextViewer.java b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslatedTextViewer.java index 4b284c53fa..28c0c64a6c 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslatedTextViewer.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/ui/TranslatedTextViewer.java @@ -341,8 +341,7 @@ public final class TranslatedTextViewer implements TextViewer { textBuilder.append(cbuf, 0, read); bytesRead += read; } - - // The trim is on here because HTML files were observed with nearly 1MB of white space at the end + return textBuilder.toString(); } From a76fdcb68e82e5cd2f2f384e5fe91c8ccb2c8c57 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Fri, 7 Jun 2019 10:17:38 -0400 Subject: [PATCH 065/106] 5129 adjustments to ui position --- .../GoogleTranslatorSettingsPanel.form | 64 ++++++++++--------- .../GoogleTranslatorSettingsPanel.java | 46 ++++++------- 2 files changed, 58 insertions(+), 52 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form index cf280d463c..28ab58da65 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.form @@ -23,40 +23,44 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java index 23aaf0902a..39c244be05 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java @@ -265,32 +265,34 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel { .addComponent(instructionsScrollPane) .addContainerGap()) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(credentialsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(targetLanguageLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(testButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(credentialsLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(targetLanguageLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(credentialsPathField) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(browseButton) - .addGap(14, 14, 14)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(credentialsPathField) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(browseButton) + .addGap(14, 14, 14)) + .addGroup(layout.createSequentialGroup() + .addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 317, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE)))) .addGroup(layout.createSequentialGroup() - .addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 317, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, Short.MAX_VALUE)))) - .addGroup(layout.createSequentialGroup() - .addComponent(testButton, javax.swing.GroupLayout.PREFERRED_SIZE, 83, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(untranslatedLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(resultLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(testResultValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGroup(layout.createSequentialGroup() - .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addGap(7, 7, 7) + .addComponent(untranslatedLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(resultLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(testResultValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) From a2515c4cb56d813580c75db54b749450ee069ce7 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Fri, 7 Jun 2019 10:26:01 -0400 Subject: [PATCH 066/106] Implemented tagging functionality for html reports and included both the tagged thumbnail and full tagged image (as well as original). --- .../contentviewers/MediaViewImagePanel.java | 13 +- .../imagetagging/ImageTagCreator.java | 6 +- .../imagetagging/ImageTagsUtility.java | 103 ++++++++++-- .../sleuthkit/autopsy/report/ReportHTML.java | 146 +++++++++++++----- 4 files changed, 206 insertions(+), 62 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index 3f33d8ff47..6a5643af2e 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.contentviewers; import java.awt.EventQueue; import java.awt.event.ActionEvent; +import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; @@ -864,15 +865,21 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan @Override protected Void doInBackground() { try { + //Retrieve content viewer tags List tags = Case.getCurrentCase().getServices() .getTagsManager().getContentTagsByContent(file); List> contentViewerTags = getContentViewerTags(tags); + + //Pull out image tag regions Collection regions = contentViewerTags.stream() .map(cvTag -> cvTag.getDetails()).collect(Collectors.toList()); - byte[] jpgImage = ImageTagsUtility.exportTags(file, regions, ".jpg"); + + //Apply tags to image and write to file + BufferedImage pngImage = ImageTagsUtility.writeTags(file, regions, "png"); Path output = Paths.get(exportChooser.getSelectedFile().getPath(), - FilenameUtils.getBaseName(file.getName()) + "-with_tags.jpg"); //NON-NLS - Files.write(output, jpgImage); + FilenameUtils.getBaseName(file.getName()) + "-with_tags.png"); //NON-NLS + ImageIO.write(pngImage, "png", output.toFile()); + JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_successfulExport()); } catch (TskCoreException | NoCurrentCaseException | IOException ex) { LOGGER.log(Level.WARNING, "Unable to export tagged image to disk", ex); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java index 885dd6c18f..24421d796c 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java @@ -41,7 +41,7 @@ public final class ImageTagCreator extends Rectangle { //Rectangle lines should be 1.5% of the image. This level of thickness has //a good balance between visual acuity and loss of selection at the borders //of the image. - private final static double lineThicknessAsPercent = 1.5; + private final static double LINE_THICKNESS_PERCENT = 1.5; private final double minArea; //Used to update listeners of the new tag boundaries @@ -68,11 +68,11 @@ public final class ImageTagCreator extends Rectangle { //Calculate how many pixels the stroke width should be to guarentee //a consistent % of image consumed by the rectangle border. double min = Math.min(image.getImage().getWidth(), image.getImage().getHeight()); - double lineThicknessPixels = min * lineThicknessAsPercent / 100.0; + double lineThicknessPixels = min * LINE_THICKNESS_PERCENT / 100.0; setStrokeWidth(lineThicknessPixels); minArea = lineThicknessPixels * lineThicknessPixels; setVisible(false); - + this.mousePressed = (MouseEvent event) -> { if (event.isSecondaryButtonDown()) { return; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java index 1d1d164e2f..3c5fccc5e6 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java @@ -18,13 +18,21 @@ */ package org.sleuthkit.autopsy.contentviewers.imagetagging; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.util.Collection; +import javax.imageio.ImageIO; import org.opencv.core.Core; import org.opencv.core.Mat; import org.opencv.core.MatOfByte; +import org.opencv.core.MatOfInt; import org.opencv.core.Point; import org.opencv.core.Scalar; +import org.opencv.core.Size; import org.opencv.highgui.Highgui; +import org.opencv.imgproc.Imgproc; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; @@ -34,19 +42,42 @@ import org.sleuthkit.datamodel.TskCoreException; public final class ImageTagsUtility { /** - * Embeds the tag regions into an image (represented as an AbstractFile). - * + * Sizes for thumbnails + */ + public enum IconSize { + SMALL(50), + MEDIUM(100), + LARGE(200); + + private final int SIZE; + + IconSize(int size) { + this.SIZE = size; + } + + public int getSize() { + return SIZE; + } + } + + /** + * Embeds the tag regions into an image. + * * @param file Base Image * @param tagRegions Tag regions to be saved into the image - * @param outputEncoding Output file type encoding (ex. .jpg, .png) - * @return output image in byte array - * @throws TskCoreException + * @param outputEncoding Format of image (jpg, png, etc). See OpenCV for + * supported formats. Do not include a "." + * @return Output image as a BufferedImage + * + * @throws TskCoreException Cannot read from abstract file + * @throws IOException Could not create buffered image from OpenCV result */ - public static byte[] exportTags(AbstractFile file, Collection tagRegions, String outputEncoding) throws TskCoreException { + public static BufferedImage writeTags(AbstractFile file, Collection tagRegions, + String outputEncoding) throws TskCoreException, IOException { byte[] imageInMemory = new byte[(int) file.getSize()]; file.read(imageInMemory, 0, file.getSize()); Mat originalImage = Highgui.imdecode(new MatOfByte(imageInMemory), Highgui.IMREAD_UNCHANGED); - + tagRegions.forEach((region) -> { Core.rectangle( originalImage, //Matrix obj of the image @@ -56,17 +87,55 @@ public final class ImageTagsUtility { (int) Math.rint(region.getStrokeThickness()) ); }); - + MatOfByte matOfByte = new MatOfByte(); - Highgui.imencode(outputEncoding, originalImage, matOfByte); - - originalImage.release(); - byte[] output = matOfByte.toArray(); - matOfByte.release(); - - return output; + MatOfInt params = new MatOfInt(Highgui.IMWRITE_JPEG_QUALITY, 100); + Highgui.imencode("." + outputEncoding, originalImage, matOfByte, params); + + try (ByteArrayInputStream imageStream = new ByteArrayInputStream(matOfByte.toArray())) { + BufferedImage result = ImageIO.read(imageStream); + originalImage.release(); + matOfByte.release(); + return result; + } } - - private ImageTagsUtility(){ + + /** + * Creates a thumbnail version of the image with tags applied. + * + * @param file Input file to apply tags & produce thumbnail from + * @param tagRegions Tags to apply + * @param iconSize Size of the output thumbnail + * @param outputEncoding Format of thumbnail (jpg, png, etc). See OpenCV for + * supported formats. Do not include a "." + * @return BufferedImage representing the thumbnail + * + * @throws TskCoreException Could not read from file + * @throws IOException Could not create buffered image from OpenCV result + */ + public static BufferedImage makeThumbnail(AbstractFile file, Collection tagRegions, + IconSize iconSize, String outputEncoding) throws TskCoreException, IOException { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + BufferedImage result = writeTags(file, tagRegions, outputEncoding); + ImageIO.write(result, outputEncoding, baos); + Mat markedUpImage = Highgui.imdecode(new MatOfByte(baos.toByteArray()), Highgui.IMREAD_UNCHANGED); + Mat thumbnail = new Mat(); + Size resize = new Size(iconSize.getSize(), iconSize.getSize()); + + Imgproc.resize(markedUpImage, thumbnail, resize); + MatOfByte matOfByte = new MatOfByte(); + Highgui.imencode("." + outputEncoding, thumbnail, matOfByte); + + try (ByteArrayInputStream thumbnailStream = new ByteArrayInputStream(matOfByte.toArray())) { + BufferedImage thumbnailImage = ImageIO.read(thumbnailStream); + thumbnail.release(); + matOfByte.release(); + markedUpImage.release(); + return thumbnailImage; + } + } + } + + private ImageTagsUtility() { } } diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index b54af094ad..896d572b99 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -48,18 +48,18 @@ import java.util.TreeMap; import java.util.logging.Level; import javax.imageio.ImageIO; import javax.swing.JPanel; +import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.openide.filesystems.FileUtil; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.casemodule.services.Services; import org.sleuthkit.autopsy.casemodule.services.TagsManager; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization; +import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager; +import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag; +import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion; +import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsUtility; import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.Logger; @@ -731,6 +731,29 @@ class ReportHTML implements TableReportModule { logger.log(Level.SEVERE, "Output writer is null. Page was not initialized before writing.", ex); //NON-NLS } } + + /** + * Finds all associated image tags. + * + * @param contentTags + * @return + */ + private List getTaggedRegions(List contentTags) { + ArrayList tagRegions = new ArrayList<>(); + contentTags.forEach((contentTag) -> { + try { + ContentViewerTag contentViewerTag = ContentViewerTagManager + .getTag(contentTag, ImageTagRegion.class); + if (contentViewerTag != null) { + tagRegions.add(contentViewerTag.getDetails()); + } + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Could not get content viewer tag " + + "from case db for content_tag with id %d", contentTag.getId()); + } + }); + return tagRegions; + } /** * Add the body of the thumbnails table. @@ -770,13 +793,54 @@ class ReportHTML implements TableReportModule { } AbstractFile file = (AbstractFile) content; + List contentTags = new ArrayList<>(); + + String thumbnailPath = null; + String imageWithTagsFullPath = null; + try { + //Get content tags and all image tags + contentTags = Case.getCurrentCase().getServices() + .getTagsManager().getContentTagsByContent(file); + List imageTags = getTaggedRegions(contentTags); + + if(!imageTags.isEmpty()) { + //Write the tags to the fullsize and thumbnail images + BufferedImage fullImageWithTags = ImageTagsUtility.writeTags(file, imageTags, "png"); + + BufferedImage thumbnailImageWithTags = ImageTagsUtility.makeThumbnail(file, + imageTags, ImageTagsUtility.IconSize.MEDIUM, "png"); + + String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(file.getName()); + + //Create paths in report to write tagged images + File thumbnailImageWithTagsFile = Paths.get(thumbsPath, FilenameUtils.removeExtension(fileName) + ".png").toFile(); + String fullImageWithTagsPath = makeCustomUniqueFilePath(file, "thumbs_fullsize"); + fullImageWithTagsPath = FilenameUtils.removeExtension(fullImageWithTagsPath) + ".png"; + File fullImageWithTagsFile = Paths.get(fullImageWithTagsPath).toFile(); + + //Save images + ImageIO.write(thumbnailImageWithTags, "png", thumbnailImageWithTagsFile); + ImageIO.write(fullImageWithTags, "png", fullImageWithTagsFile); + + thumbnailPath = THUMBS_REL_PATH + thumbnailImageWithTagsFile.getName(); + //Relative path + imageWithTagsFullPath = fullImageWithTagsPath.substring(subPath.length()); + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Could not get tags for file.", ex); //NON-NLS + } catch (IOException ex) { + logger.log(Level.WARNING, "Could make marked up thumbnail.", ex); //NON-NLS + } // save copies of the orginal image and thumbnail image - String thumbnailPath = prepareThumbnail(file); + if(thumbnailPath == null) { + thumbnailPath = prepareThumbnail(file); + } + if (thumbnailPath == null) { continue; } - String contentPath = saveContent(file, "thumbs_fullsize"); //NON-NLS + String contentPath = saveContent(file, "original"); //NON-NLS String nameInImage; try { nameInImage = file.getUniquePath(); @@ -787,30 +851,27 @@ class ReportHTML implements TableReportModule { StringBuilder linkToThumbnail = new StringBuilder(); linkToThumbnail.append(""); currentRow.add(linkToThumbnail.toString()); @@ -839,17 +900,8 @@ class ReportHTML implements TableReportModule { || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS; } - - /** - * Save a local copy of the given file in the reports folder. - * - * @param file File to save - * @param dirName Custom top-level folder to use to store the files in (tag - * name, etc.) - * - * @return Path to where file was stored (relative to root of HTML folder) - */ - public String saveContent(AbstractFile file, String dirName) { + + private String makeCustomUniqueFilePath(AbstractFile file, String dirName) { // clean up the dir name passed in String dirName2 = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(dirName); @@ -883,16 +935,32 @@ class ReportHTML implements TableReportModule { } localFilePath.append(File.separator); localFilePath.append(fileName); + + return localFilePath.toString(); + } + + /** + * Save a local copy of the given file in the reports folder. + * + * @param file File to save + * @param dirName Custom top-level folder to use to store the files in (tag + * name, etc.) + * + * @return Path to where file was stored (relative to root of HTML folder) + */ + public String saveContent(AbstractFile file, String dirName) { + + String localFilePath = makeCustomUniqueFilePath(file, dirName); // If the local file doesn't already exist, create it now. // The existence check is necessary because it is possible to apply multiple tags with the same tagName to a file. - File localFile = new File(localFilePath.toString()); + File localFile = new File(localFilePath); if (!localFile.exists()) { ExtractFscContentVisitor.extract(file, localFile, null, null); } // get the relative path - return localFilePath.toString().substring(subPath.length()); + return localFilePath.substring(subPath.length()); } /** From 605e6959921c161fcf584f32ff2d7508a833c940 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Fri, 7 Jun 2019 10:43:13 -0400 Subject: [PATCH 067/106] Use enum for SCO column names. Escape quotes. --- .../autopsy/directorytree/ExportCSVAction.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java index 4a87f72828..759078fe2b 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java @@ -47,6 +47,7 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType; import org.openide.nodes.Node; import org.openide.nodes.Node.PropertySet; import org.openide.nodes.Node.Property; @@ -58,7 +59,8 @@ public final class ExportCSVAction extends AbstractAction { private static Logger logger = Logger.getLogger(ExportCSVAction.class.getName()); private final static String DEFAULT_FILENAME = "Results"; - private final static List columnsToSkip = Arrays.asList("S", "C", "O"); + private final static List columnsToSkip = Arrays.asList(AbstractFilePropertyType.SCORE.toString(), + AbstractFilePropertyType.COMMENT.toString(), AbstractFilePropertyType.OCCURRENCES.toString()); private static String userDefinedExportPath; @@ -300,7 +302,7 @@ public final class ExportCSVAction extends AbstractAction { for(PropertySet set : sets) { for (Property prop : set.getProperties()) { if ( ! columnsToSkip.contains(prop.getDisplayName())) { - values.add(prop.getValue().toString()); + values.add(escapeQuotes(prop.getValue().toString())); } } } @@ -311,6 +313,18 @@ public final class ExportCSVAction extends AbstractAction { return null; } + /** + * Escape any quotes in the string + * + * @param original + * + * @return the string with quotes escaped + */ + private String escapeQuotes(String original) { + String result = original.replaceAll("\"", "\\\\\""); + return result; + } + /** * Convert list of values to a comma separated string. * From f19a9a6c3cc6d0de5bc072194b7a37057c2e866e Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Fri, 7 Jun 2019 10:50:56 -0400 Subject: [PATCH 068/106] Codacy fixes --- .../contentviewers/MediaViewImagePanel.java | 1 - .../org/sleuthkit/autopsy/report/ReportHTML.java | 16 ++++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index 6a5643af2e..b48460e9ba 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -25,7 +25,6 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index 896d572b99..bb5de222cb 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -849,18 +849,18 @@ class ReportHTML implements TableReportModule { } StringBuilder linkToThumbnail = new StringBuilder(); - linkToThumbnail.append("