diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form index 22f3d74c28..57670e96aa 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form @@ -16,13 +16,25 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -215,5 +85,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java index 041cd3fc31..62af39f3a2 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java @@ -20,6 +20,9 @@ package org.sleuthkit.autopsy.casemodule; import java.awt.*; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ImageIcon; @@ -64,16 +67,16 @@ public class CueBannerPanel extends javax.swing.JPanel { // //GEN-BEGIN:initComponents private void initComponents() { - closeButton = new javax.swing.JButton(); - editorPanel = new javax.swing.JPanel(); + autopsyLogo = new javax.swing.JLabel(); + this.autopsyLogo.setText(""); newCaseButton = new javax.swing.JButton(); openRecentButton = new javax.swing.JButton(); createNewLabel = new javax.swing.JLabel(); openRecentLabel = new javax.swing.JLabel(); openCaseButton = new javax.swing.JButton(); openLabel = new javax.swing.JLabel(); - autopsyLogo = new javax.swing.JLabel(); - this.autopsyLogo.setText(""); + closeButton = new javax.swing.JButton(); + jSeparator1 = new javax.swing.JSeparator(); closeButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.closeButton.text")); // NOI18N @@ -82,7 +85,7 @@ public class CueBannerPanel extends javax.swing.JPanel { newCaseButton.setBorder(null); newCaseButton.setBorderPainted(false); newCaseButton.setContentAreaFilled(false); - newCaseButton.setPreferredSize(new java.awt.Dimension(70, 70)); + newCaseButton.setPreferredSize(new java.awt.Dimension(64, 64)); newCaseButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { newCaseButtonActionPerformed(evt); @@ -94,7 +97,7 @@ public class CueBannerPanel extends javax.swing.JPanel { openRecentButton.setBorder(null); openRecentButton.setBorderPainted(false); openRecentButton.setContentAreaFilled(false); - openRecentButton.setPreferredSize(new java.awt.Dimension(70, 70)); + openRecentButton.setPreferredSize(new java.awt.Dimension(64, 64)); openRecentButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { openRecentButtonActionPerformed(evt); @@ -113,7 +116,7 @@ public class CueBannerPanel extends javax.swing.JPanel { openCaseButton.setBorderPainted(false); openCaseButton.setContentAreaFilled(false); openCaseButton.setMargin(new java.awt.Insets(1, 1, 1, 1)); - openCaseButton.setPreferredSize(new java.awt.Dimension(70, 70)); + openCaseButton.setPreferredSize(new java.awt.Dimension(64, 64)); openCaseButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { openCaseButtonActionPerformed(evt); @@ -123,46 +126,10 @@ public class CueBannerPanel extends javax.swing.JPanel { openLabel.setFont(openLabel.getFont().deriveFont(Font.PLAIN, 13)); openLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openLabel.text")); // NOI18N - javax.swing.GroupLayout editorPanelLayout = new javax.swing.GroupLayout(editorPanel); - editorPanel.setLayout(editorPanelLayout); - editorPanelLayout.setHorizontalGroup( - editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(editorPanelLayout.createSequentialGroup() - .addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(editorPanelLayout.createSequentialGroup() - .addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(createNewLabel)) - .addGroup(editorPanelLayout.createSequentialGroup() - .addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(openRecentLabel)) - .addGroup(editorPanelLayout.createSequentialGroup() - .addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(openLabel))) - .addContainerGap(60, Short.MAX_VALUE)) - ); - editorPanelLayout.setVerticalGroup( - editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(editorPanelLayout.createSequentialGroup() - .addGap(32, 32, 32) - .addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) - .addComponent(createNewLabel) - .addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) - .addComponent(openRecentLabel) - .addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(0, 0, Short.MAX_VALUE) - .addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) - .addComponent(openLabel) - .addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(25, Short.MAX_VALUE)) - ); autopsyLogo.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/welcome_logo.png"))); // NOI18N NON-NLS autopsyLogo.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.autopsyLogo.text")); // NOI18N + jSeparator1.setOrientation(javax.swing.SwingConstants.VERTICAL); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); @@ -171,23 +138,44 @@ public class CueBannerPanel extends javax.swing.JPanel { .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(autopsyLogo) - .addGap(29, 29, 29) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(editorPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(closeButton, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap()) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 5, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) + .addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(openCaseButton, 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.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(createNewLabel) + .addComponent(openRecentLabel) + .addComponent(openLabel)) + .addComponent(closeButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(15, 15, 15)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(autopsyLogo, javax.swing.GroupLayout.PREFERRED_SIZE, 257, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addGroup(layout.createSequentialGroup() - .addComponent(editorPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addComponent(closeButton))) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) + .addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(createNewLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) + .addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(openRecentLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) + .addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(openLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(closeButton)) + .addComponent(jSeparator1) + .addComponent(autopsyLogo, javax.swing.GroupLayout.PREFERRED_SIZE, 257, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap()) ); }// //GEN-END:initComponents @@ -235,7 +223,7 @@ public class CueBannerPanel extends javax.swing.JPanel { private javax.swing.JLabel autopsyLogo; private javax.swing.JButton closeButton; private javax.swing.JLabel createNewLabel; - private javax.swing.JPanel editorPanel; + private javax.swing.JSeparator jSeparator1; private javax.swing.JButton newCaseButton; private javax.swing.JButton openCaseButton; private javax.swing.JLabel openLabel; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/welcome_logo.png b/Core/src/org/sleuthkit/autopsy/casemodule/welcome_logo.png index 5bac99e039..220f6f3394 100644 Binary files a/Core/src/org/sleuthkit/autopsy/casemodule/welcome_logo.png and b/Core/src/org/sleuthkit/autopsy/casemodule/welcome_logo.png differ diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java index 0b6c28e7e8..96f38aaae5 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2013 Basis Technology Corp. + * Copyright 2011-2014 Basis Technology Corp. * Contact: carrier sleuthkit org *s * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,22 +21,20 @@ package org.sleuthkit.autopsy.corecomponents; import java.awt.CardLayout; import java.awt.Component; import java.awt.Dimension; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import java.util.logging.Level; -import javax.imageio.ImageIO; - -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.coreutils.Logger; import org.openide.nodes.Node; +import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProviders; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.coreutils.ImageUtils; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.AbstractFile.MimeMatchEnum; import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; /** @@ -47,14 +45,15 @@ import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; }) public class DataContentViewerMedia extends javax.swing.JPanel implements DataContentViewer { - private static final String[] AUDIO_EXTENSIONS = new String[]{".mp3", ".wav", ".wma"}; //NON-NLS + private static final Set AUDIO_EXTENSIONS = new TreeSet<>(Arrays.asList(".mp3", ".wav", ".wma")); //NON-NLS private static final Logger logger = Logger.getLogger(DataContentViewerMedia.class.getName()); private AbstractFile lastFile; //UI private final MediaViewVideoPanel videoPanel; - private final String[] videoExtensions; // get them from the panel - private String[] imageExtensions; // use javafx supported - private final List supportedMimes; + private final SortedSet videoExtensions; // get them from the panel + private final SortedSet imageExtensions; + private final SortedSet videoMimes; + private final SortedSet imageMimes; private final MediaViewImagePanel imagePanel; private boolean videoPanelInited; private boolean imagePanelInited; @@ -70,34 +69,24 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo // get the right panel for our platform videoPanel = MediaViewVideoPanel.createVideoPanel(); + videoPanelInited = videoPanel.isInited(); + videoExtensions = new TreeSet<>(Arrays.asList(videoPanel.getExtensions())); + videoMimes = new TreeSet<>(videoPanel.getMimeTypes()); imagePanel = new MediaViewImagePanel(); - videoPanelInited = videoPanel.isInited(); imagePanelInited = imagePanel.isInited(); - - videoExtensions = videoPanel.getExtensions(); - supportedMimes = videoPanel.getMimeTypes(); + imageMimes = new TreeSet<>(imagePanel.getMimeTypes()); + imageExtensions = new TreeSet<>(imagePanel.getExtensions()); + customizeComponents(); logger.log(Level.INFO, "Created MediaView instance: " + this); //NON-NLS } private void customizeComponents() { - //initialize supported image types - //TODO use mime-types instead once we have support - String[] fxSupportedImagesSuffixes = ImageIO.getReaderFileSuffixes(); - imageExtensions = new String[fxSupportedImagesSuffixes.length]; - //logger.log(Level.INFO, "Supported image formats by javafx image viewer: "); - for (int i = 0; i < fxSupportedImagesSuffixes.length; ++i) { - String suffix = fxSupportedImagesSuffixes[i]; - //logger.log(Level.INFO, "suffix: " + suffix); - imageExtensions[i] = "." + suffix; - } - add(imagePanel, IMAGE_VIEWER_LAYER); add(videoPanel, VIDEO_VIEWER_LAYER); - switchPanels(false); - + showVideoPanel(false); } /** @@ -137,19 +126,12 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo final Dimension dims = DataContentViewerMedia.this.getSize(); //logger.info("setting node on media viewer"); //NON-NLS - if (imagePanelInited && containsExt(file.getName(), imageExtensions)) { + if (imagePanelInited && isImageSupported(file)) { imagePanel.showImageFx(file, dims); - this.switchPanels(false); - } else if (imagePanelInited && ImageUtils.isJpegFileHeader(file)) { - - imagePanel.showImageFx(file, dims); - this.switchPanels(false); - - } else if (videoPanelInited - && containsMimeType(selectedNode,supportedMimes)&&(containsExt(file.getName(), videoExtensions) || containsExt(file.getName(), AUDIO_EXTENSIONS))) { + this.showVideoPanel(false); + } else if (videoPanelInited && isVideoSupported(file)) { videoPanel.setupVideo(file, dims); - switchPanels(true); - + this.showVideoPanel(true); } } catch (Exception e) { logger.log(Level.SEVERE, "Exception while setting node", e); //NON-NLS @@ -161,7 +143,7 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo * * @param showVideo true if video panel, false if image panel */ - private void switchPanels(boolean showVideo) { + private void showVideoPanel(boolean showVideo) { CardLayout layout = (CardLayout) this.getLayout(); if (showVideo) { layout.show(this, VIDEO_VIEWER_LAYER); @@ -197,6 +179,57 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo lastFile = null; } + /** + * + * @param file + * @return True if a video file that can be displayed + */ + private boolean isVideoSupported(AbstractFile file) { + String name = file.getName().toLowerCase(); + + if ((containsExt(name, AUDIO_EXTENSIONS) || containsExt(name, videoExtensions)) && + (!videoMimes.isEmpty() && file.isMimeType(videoMimes) == MimeMatchEnum.TRUE)) { + return true; + } + return false; + } + + /** + * + * @param file + * @return True if an image file that can be displayed + */ + private boolean isImageSupported(AbstractFile file) { + String name = file.getName().toLowerCase(); + + // blackboard + if (!imageMimes.isEmpty()) { + MimeMatchEnum mimeMatch = file.isMimeType(imageMimes); + if (mimeMatch == MimeMatchEnum.TRUE) { + return true; + } + else if (mimeMatch == MimeMatchEnum.FALSE) { + return false; + } + } + + // extension + if (containsExt(name, imageExtensions)) { + return true; + } + // our own signature checks for important types + else if (ImageUtils.isJpegFileHeader(file)) { + return true; + } + else if (ImageUtils.isPngFileHeader(file)) { + return true; + } + + //for gstreamer formats, check if initialized first, then + //support audio formats, and video formats + return false; + } + @Override public boolean isSupported(Node node) { if (node == null) { @@ -211,23 +244,15 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo if (file.getSize() == 0) { return false; } - String name = file.getName().toLowerCase(); + if (imagePanelInited) { - if (containsExt(name, imageExtensions)) { + if (isImageSupported(file)) return true; - } - else if (ImageUtils.isJpegFileHeader(file)) { - return true; - } - //for gstreamer formats, check if initialized first, then - //support audio formats, and video formats } if (videoPanelInited && videoPanel.isInited()) { - if ((containsExt(name, AUDIO_EXTENSIONS) - || containsExt(name, videoExtensions))&& containsMimeType(node,supportedMimes)) { + if (isVideoSupported(file)) return true; - } } return false; @@ -252,34 +277,12 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo } - private static boolean containsExt(String name, String[] exts) { + private static boolean containsExt(String name, Set exts) { int extStart = name.lastIndexOf("."); String ext = ""; if (extStart != -1) { ext = name.substring(extStart, name.length()).toLowerCase(); } - return Arrays.asList(exts).contains(ext); - } - private static boolean containsMimeType(Node node, List mimeTypes) { - if (mimeTypes.isEmpty()) { - //this should return true for 32 bit autopsy with the gstreamer MimeTypes list being empty, - //since mimetype detection is for java fx only. For 64 bit java fx, the code continues for Mimetype detection. - return true; - } - AbstractFile file = node.getLookup().lookup(AbstractFile.class); - try { - ArrayList genInfoAttributes = file.getGenInfoAttributes(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG); - if (genInfoAttributes.isEmpty() == false) { - for (BlackboardAttribute batt : genInfoAttributes) { - if (mimeTypes.contains(batt.getValueString())) { - return true; - } - } - return false; - } - } catch (TskCoreException ex) { - return false; - } - return false; + return exts.contains(ext); } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.java index 4f8b8e299e..0d9cc34d45 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.java @@ -80,9 +80,7 @@ import org.sleuthkit.datamodel.TskData; public class FXVideoPanel extends MediaViewVideoPanel { private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".mpg", ".mpeg"}; //NON-NLS - - static private final List supportedMimes = Arrays.asList("audio/x-aiff", "video/x-javafx", "video/x-flv", "application/vnd.apple.mpegurl", " audio/mpegurl", "audio/mpeg", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS - + private static final List MIMETYPES = Arrays.asList("audio/x-aiff", "video/x-javafx", "video/x-flv", "application/vnd.apple.mpegurl", " audio/mpegurl", "audio/mpeg", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName()); private boolean fxInited = false; @@ -825,6 +823,6 @@ public class FXVideoPanel extends MediaViewVideoPanel { @Override public List getMimeTypes() { - return supportedMimes; + return MIMETYPES; } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.java index 6201734fb6..db5f97b40f 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.java @@ -52,8 +52,6 @@ import org.gstreamer.elements.RGBDataSink; import org.gstreamer.swing.VideoComponent; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; -import org.openide.DialogDisplayer; -import org.openide.NotifyDescriptor; import org.openide.util.Cancellable; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; @@ -71,8 +69,8 @@ import org.sleuthkit.datamodel.TskData; @ServiceProvider(service = FrameCapture.class) }) public class GstVideoPanel extends MediaViewVideoPanel { - - private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"}; //NON-NLS + private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"}; //NON-NLS + private static final List MIMETYPES = Arrays.asList("video/quicktime", "audio/mpeg", "audio/x-mpeg", "video/mpeg", "video/x-mpeg", "audio/mpeg3", "audio/x-mpeg-3", "video/x-flv", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS private static final Logger logger = Logger.getLogger(GstVideoPanel.class.getName()); private boolean gstInited; @@ -88,8 +86,8 @@ public class GstVideoPanel extends MediaViewVideoPanel { private boolean autoTracking = false; // true if the slider is moving automatically private final Object playbinLock = new Object(); // lock for synchronization of gstPlaybin2 player private AbstractFile currentFile; - private Set badVideoFiles = Collections.synchronizedSet(new HashSet()); - static private final List supportedMimes = Arrays.asList(); + private final Set badVideoFiles = Collections.synchronizedSet(new HashSet()); + /** * Creates new form MediaViewVideoPanel */ @@ -806,6 +804,6 @@ public class GstVideoPanel extends MediaViewVideoPanel { @Override public List getMimeTypes() { - return supportedMimes; + return MIMETYPES; } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java index 50d4461939..b1dfe667d5 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java @@ -23,6 +23,9 @@ import java.awt.EventQueue; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.logging.Level; import javafx.application.Platform; import javafx.embed.swing.JFXPanel; @@ -33,7 +36,6 @@ import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javax.imageio.ImageIO; import javax.swing.SwingUtilities; - import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.corelibs.ScalrWrapper; @@ -47,26 +49,30 @@ import org.sleuthkit.datamodel.ReadContentInputStream; * used with JavaFx image viewer only. */ public class MediaViewImagePanel extends javax.swing.JPanel { - private JFXPanel fxPanel; private ImageView fxImageView; private static final Logger logger = Logger.getLogger(MediaViewImagePanel.class.getName()); private boolean fxInited = false; + + private final List supportedExtensions; + static private final List supportedMimes = Arrays.asList("image/jpeg", "image/png", "image/gif", "image/bmp"); /** * Creates new form MediaViewImagePanel */ public MediaViewImagePanel() { initComponents(); - - - fxInited = org.sleuthkit.autopsy.core.Installer.isJavaFxInited(); - - if (fxInited) { setupFx(); } + + supportedExtensions = new ArrayList<>(); + //logger.log(Level.INFO, "Supported image formats by javafx image viewer: "); + for (String suffix : ImageIO.getReaderFileSuffixes()) { + //logger.log(Level.INFO, "suffix: " + suffix); + supportedExtensions.add("." + suffix); + } } public boolean isInited() { @@ -99,13 +105,8 @@ import org.sleuthkit.datamodel.ReadContentInputStream; // setVisible(true); } }); - - - } }); - - } public void reset() { @@ -207,6 +208,22 @@ import org.sleuthkit.datamodel.ReadContentInputStream; }); } + + /** + * returns supported mime types + * @return + */ + public List getMimeTypes() { + return supportedMimes; + } + + /** + * returns supported extensions (each starting with .) + * @return + */ + public List getExtensions() { + return supportedExtensions; + } /** * This method is called from within the constructor to initialize the form. @@ -222,4 +239,4 @@ import org.sleuthkit.datamodel.ReadContentInputStream; }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables // End of variables declaration//GEN-END:variables -} +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java index 69f357bc20..b5944cd2b4 100755 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java @@ -196,6 +196,32 @@ public class ImageUtils { return (((fileHeaderBuffer[0] & 0xff) == 0xff) && ((fileHeaderBuffer[1] & 0xff) == 0xd8)); } + public static boolean isPngFileHeader(AbstractFile file) { + if (file.getSize() < 10) { + return false; + } + + byte[] fileHeaderBuffer = new byte[8]; + int bytesRead; + try { + bytesRead = file.read(fileHeaderBuffer, 0, 8); + } catch (TskCoreException ex) { + //ignore if can't read the first few bytes, not an image + return false; + } + if (bytesRead != 8) { + return false; + } + /* + * Check for the header. Since Java bytes are signed, we cast them + * to an int first. + */ + return (((fileHeaderBuffer[1] & 0xff) == 0x50) && ((fileHeaderBuffer[2] & 0xff) == 0x4E) && + ((fileHeaderBuffer[3] & 0xff) == 0x47) && ((fileHeaderBuffer[4] & 0xff) == 0x0D) && + ((fileHeaderBuffer[5] & 0xff) == 0x0A) && ((fileHeaderBuffer[6] & 0xff) == 0x1A) && + ((fileHeaderBuffer[7] & 0xff) == 0x0A)); + } + private static Image generateAndSaveIcon(Content content, int iconSize) { Image icon = null; diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java index 88c954af83..7a14d02610 100755 --- a/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java @@ -99,8 +99,8 @@ class SampleDataSourceIngestModule implements DataSourceIngestModule { // Get files by creation time. long currentTime = System.currentTimeMillis() / 1000; long minTime = currentTime - (14 * 24 * 60 * 60); // Go back two weeks. - List otherFiles = sleuthkitCase.findFilesWhere("crtime > " + minTime); - for (FsContent otherFile : otherFiles) { + List otherFiles = fileManager.findFiles(dataSource, "crtime > " + minTime); + for (AbstractFile otherFile : otherFiles) { if (!skipKnownFiles || otherFile.getKnown() != TskData.FileKnown.KNOWN) { ++fileCount; } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestPipeline.java b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestPipeline.java index 34ddf4c603..71fb38b13a 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestPipeline.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestPipeline.java @@ -39,31 +39,13 @@ final class DataSourceIngestPipeline { this.job = job; // Create an ingest module instance from each data source ingest module - // template. Put the modules in a map of module class names to module - // instances to facilitate loading the modules into the pipeline in the - // sequence indicated by the ordered list of module class names that - // will be obtained from the data source ingest pipeline configuration. - Map modulesByClass = new HashMap<>(); + // template. for (IngestModuleTemplate template : moduleTemplates) { if (template.isDataSourceIngestModuleTemplate()) { DataSourceIngestModuleDecorator module = new DataSourceIngestModuleDecorator(template.createDataSourceIngestModule(), template.getModuleName()); - modulesByClass.put(module.getClassName(), module); + modules.add(module); } } - - // Add the ingest modules to the pipeline in the order indicated by the - // data source ingest pipeline configuration, adding any additional - // modules found in the global lookup, but not mentioned in the - // configuration, to the end of the pipeline in arbitrary order. - List pipelineConfig = IngestPipelinesConfiguration.getInstance().getDataSourceIngestPipelineConfig(); - for (String moduleClassName : pipelineConfig) { - if (modulesByClass.containsKey(moduleClassName)) { - modules.add(modulesByClass.remove(moduleClassName)); - } - } - for (DataSourceIngestModuleDecorator module : modulesByClass.values()) { - modules.add(module); - } } boolean isEmpty() { diff --git a/Core/src/org/sleuthkit/autopsy/ingest/FileIngestPipeline.java b/Core/src/org/sleuthkit/autopsy/ingest/FileIngestPipeline.java index 1a02e29e55..795ff0b1b9 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/FileIngestPipeline.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/FileIngestPipeline.java @@ -37,34 +37,12 @@ final class FileIngestPipeline { // Create an ingest module instance from each file ingest module // template. - // current code uses the order pased in. - // Commented out code relied on the XML configuration file for ordering. - //Map modulesByClass = new HashMap<>(); for (IngestModuleTemplate template : moduleTemplates) { if (template.isFileIngestModuleTemplate()) { FileIngestModuleDecorator module = new FileIngestModuleDecorator(template.createFileIngestModule(), template.getModuleName()); modules.add(module); - //modulesByClass.put(module.getClassName(), module); } } - - // Add the ingest modules to the pipeline in the order indicated by the - // data source ingest pipeline configuration, adding any additional - // modules found in the global lookup, but not mentioned in the - // configuration, to the end of the pipeline in arbitrary order. - /*List pipelineConfig = IngestPipelinesConfiguration.getInstance().getFileIngestPipelineConfig(); - for (String moduleClassName : pipelineConfig) { - if (modulesByClass.containsKey(moduleClassName)) { - modules.add(modulesByClass.remove(moduleClassName)); - } - else { - // @@@ add error message to flag renamed / removed modules - } - } - for (FileIngestModuleDecorator module : modulesByClass.values()) { - modules.add(module); - } - */ } boolean isEmpty() { diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobConfigurator.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobConfigurator.java index b27afc2178..f9aaf17aef 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobConfigurator.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobConfigurator.java @@ -125,9 +125,9 @@ public final class IngestJobConfigurator { ModuleSettings.setConfigSetting(launcherContext, DISABLED_INGEST_MODULES_KEY, makeCommaSeparatedList(disabledModuleNames)); // Get the process unallocated space flag setting. If the setting does - // not exist yet, default it to false. + // not exist yet, default it to true. if (ModuleSettings.settingExists(launcherContext, PARSE_UNALLOC_SPACE_KEY) == false) { - ModuleSettings.setConfigSetting(launcherContext, PARSE_UNALLOC_SPACE_KEY, "false"); //NON-NLS + ModuleSettings.setConfigSetting(launcherContext, PARSE_UNALLOC_SPACE_KEY, "true"); //NON-NLS } boolean processUnallocatedSpace = Boolean.parseBoolean(ModuleSettings.getConfigSetting(launcherContext, PARSE_UNALLOC_SPACE_KEY)); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java index 3ae631dd87..e7ce367a64 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.TreeMap; import java.util.logging.Level; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; @@ -41,7 +42,7 @@ import org.sleuthkit.autopsy.modules.sevenzip.ArchiveFileExtractorModuleFactory; import org.sleuthkit.autopsy.python.JythonModuleLoader; /** - * Discovers ingest module factories implemented in Java or Jython. + * Discovers and instantiates ingest module factories. */ final class IngestModuleFactoryLoader { @@ -111,9 +112,13 @@ final class IngestModuleFactoryLoader { } } - // Add any remaining non-core factories discovered. Order is not - // guaranteed! - factories.addAll(javaFactoriesByClass.values()); + // Add any remaining non-core factories discovered. Order with an + // alphabetical sort by module display name. + TreeMap javaFactoriesSortedByName = new TreeMap<>(); + for (IngestModuleFactory factory : javaFactoriesByClass.values()) { + javaFactoriesSortedByName.put(factory.getModuleDisplayName(), factory); + } + factories.addAll(javaFactoriesSortedByName.values()); // Add any ingest module factories implemented using Jython. Order is // not guaranteed! diff --git a/Core/src/org/sleuthkit/autopsy/modules/sevenzip/SevenZipIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/sevenzip/SevenZipIngestModule.java index 35c5685ca0..1d02f1c5d8 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/sevenzip/SevenZipIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/sevenzip/SevenZipIngestModule.java @@ -60,6 +60,8 @@ import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter; +import net.sf.sevenzipjbinding.ArchiveFormat; +import static net.sf.sevenzipjbinding.ArchiveFormat.RAR; /** * 7Zip ingest module extracts supported archives, adds extracted DerivedFiles, @@ -260,6 +262,53 @@ public final class SevenZipIngestModule implements FileIngestModule { return false; } } + + /** + * Check file extension and return appropriate input options for SevenZip.openInArchive() + * + * @param archiveFile file to check file extension + * @return input parameter for SevenZip.openInArchive() + */ + private ArchiveFormat get7ZipOptions(AbstractFile archiveFile) + { + // try to get the file type from the BB + String detectedFormat = null; + try { + ArrayList attributes = archiveFile.getGenInfoAttributes(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG); + for (BlackboardAttribute attribute : attributes) { + detectedFormat = attribute.getValueString(); + break; + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Couldn't obtain file attributes for file: " + archiveFile.toString(), ex); + } + + if (detectedFormat == null) { + logger.log(Level.WARNING, "Could not detect format for file: " + archiveFile); //NON-NLS + + // if we don't have attribute info then use file extension + String extension = archiveFile.getNameExtension(); + if ("rar".equals(extension)) + { + // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive + // it will be opened incorrectly when using 7zip's built-in auto-detect functionality + return RAR; + } + + // Otherwise open the archive using 7zip's built-in auto-detect functionality + return null; + } + else if (detectedFormat.contains("application/x-rar-compressed")) + { + // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive + // it will be opened incorrectly when using 7zip's built-in auto-detect functionality + return RAR; + } + + // Otherwise open the archive using 7zip's built-in auto-detect functionality + return null; + } + /** * Unpack the file to local folder and return a list of derived files @@ -301,8 +350,12 @@ public final class SevenZipIngestModule implements FileIngestModule { boolean progressStarted = false; try { stream = new SevenZipContentReadStream(new ReadContentInputStream(archiveFile)); - inArchive = SevenZip.openInArchive(null, // autodetect archive type - stream); + + // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive + // it will be opened incorrectly when using 7zip's built-in auto-detect functionality. + // All other archive formats are still opened using 7zip built-in auto-detect functionality. + ArchiveFormat options = get7ZipOptions(archiveFile); + inArchive = SevenZip.openInArchive(options, stream); int numItems = inArchive.getNumberOfItems(); logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{archiveFile.getName(), numItems}); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/report/images/default_generator_logo.png b/Core/src/org/sleuthkit/autopsy/report/images/default_generator_logo.png index 5bac99e039..d73d6314c2 100644 Binary files a/Core/src/org/sleuthkit/autopsy/report/images/default_generator_logo.png and b/Core/src/org/sleuthkit/autopsy/report/images/default_generator_logo.png differ diff --git a/Core/src/org/sleuthkit/autopsy/report/images/favicon.ico b/Core/src/org/sleuthkit/autopsy/report/images/favicon.ico index efad7a3de4..696550c9ff 100644 Binary files a/Core/src/org/sleuthkit/autopsy/report/images/favicon.ico and b/Core/src/org/sleuthkit/autopsy/report/images/favicon.ico differ diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index 6780af841a..cd5cb85bee 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -23,8 +23,10 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.sql.ResultSet; import java.sql.SQLException; +import java.text.NumberFormat; import java.time.ZoneId; import java.util.Collection; +import java.util.Map; import java.util.MissingResourceException; import java.util.TimeZone; import java.util.concurrent.ExecutionException; @@ -92,7 +94,7 @@ import org.sleuthkit.datamodel.TskCoreException; * *
    */ -public class TimeLineController { +public class TimeLineController { private static final Logger LOGGER = Logger.getLogger(TimeLineController.class.getName()); @@ -221,9 +223,9 @@ public class TimeLineController { filteredEvents = eventsRepository.getEventsModel(); InitialZoomState = new ZoomParams(filteredEvents.getSpanningInterval(), - EventTypeZoomLevel.BASE_TYPE, - Filter.getDefaultFilter(), - DescriptionLOD.SHORT); + EventTypeZoomLevel.BASE_TYPE, + Filter.getDefaultFilter(), + DescriptionLOD.SHORT); historyManager.advance(InitialZoomState); //persistent listener instances @@ -466,13 +468,30 @@ public class TimeLineController { } } - synchronized public void pushDescrLOD(DescriptionLOD newLOD) { - ZoomParams currentZoom = filteredEvents.getRequestedZoomParamters().get(); - if (currentZoom == null) { - advance(InitialZoomState.withDescrLOD(newLOD)); - } else if (currentZoom.hasDescrLOD(newLOD) == false) { - advance(currentZoom.withDescrLOD(newLOD)); + synchronized public boolean pushDescrLOD(DescriptionLOD newLOD) { + Map eventCounts = filteredEvents.getEventCounts(filteredEvents.getRequestedZoomParamters().get().getTimeRange()); + final Long count = eventCounts.values().stream().reduce(0l, Long::sum); + + boolean shouldContinue = true; + if (newLOD == DescriptionLOD.FULL && count > 10_000) { + + int showConfirmDialog = JOptionPane.showConfirmDialog(mainFrame, + "You are about to show details for " + NumberFormat.getInstance().format(count) + " events. This might be very slow or even crash Autopsy.\n\nDo you want to continue?", + "", + JOptionPane.YES_NO_OPTION); + + shouldContinue = (showConfirmDialog == JOptionPane.YES_OPTION); } + + if (shouldContinue) { + ZoomParams currentZoom = filteredEvents.getRequestedZoomParamters().get(); + if (currentZoom == null) { + advance(InitialZoomState.withDescrLOD(newLOD)); + } else if (currentZoom.hasDescrLOD(newLOD) == false) { + advance(currentZoom.withDescrLOD(newLOD)); + } + } + return shouldContinue; } synchronized public void pushTimeAndType(Interval timeRange, EventTypeZoomLevel typeZoom) { @@ -599,35 +618,35 @@ public class TimeLineController { */ synchronized public boolean outOfDatePromptAndRebuild() { return showOutOfDateConfirmation() == JOptionPane.YES_OPTION - ? rebuildRepo() - : false; + ? rebuildRepo() + : false; } synchronized int showLastPopulatedWhileIngestingConfirmation() { return JOptionPane.showConfirmDialog(mainFrame, - DO_REPOPULATE_MESSAGE, - "re populate events?", - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE); + DO_REPOPULATE_MESSAGE, + "re populate events?", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); } synchronized int showOutOfDateConfirmation() throws MissingResourceException, HeadlessException { return JOptionPane.showConfirmDialog(mainFrame, - NbBundle.getMessage(TimeLineController.class, - "Timeline.propChg.confDlg.timelineOOD.msg"), - NbBundle.getMessage(TimeLineController.class, - "Timeline.propChg.confDlg.timelineOOD.details"), - JOptionPane.YES_NO_OPTION); + NbBundle.getMessage(TimeLineController.class, + "Timeline.propChg.confDlg.timelineOOD.msg"), + NbBundle.getMessage(TimeLineController.class, + "Timeline.propChg.confDlg.timelineOOD.details"), + JOptionPane.YES_NO_OPTION); } synchronized int showIngestConfirmation() throws MissingResourceException, HeadlessException { return JOptionPane.showConfirmDialog(mainFrame, - NbBundle.getMessage(TimeLineController.class, - "Timeline.initTimeline.confDlg.genBeforeIngest.msg"), - NbBundle.getMessage(TimeLineController.class, - "Timeline.initTimeline.confDlg.genBeforeIngest.details"), - JOptionPane.YES_NO_OPTION); + NbBundle.getMessage(TimeLineController.class, + "Timeline.initTimeline.confDlg.genBeforeIngest.msg"), + NbBundle.getMessage(TimeLineController.class, + "Timeline.initTimeline.confDlg.genBeforeIngest.details"), + JOptionPane.YES_NO_OPTION); } private class AutopsyIngestModuleListener implements PropertyChangeListener { @@ -670,7 +689,7 @@ public class TimeLineController { } @Immutable - class AutopsyCaseListener implements PropertyChangeListener { + private class AutopsyCaseListener implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent evt) { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/events/AggregateEvent.java b/Core/src/org/sleuthkit/autopsy/timeline/events/AggregateEvent.java index c97008328d..454b90975c 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/events/AggregateEvent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/events/AggregateEvent.java @@ -108,7 +108,7 @@ public class AggregateEvent { return new AggregateEvent(IntervalUtils.span(ag1.span, ag2.span), ag1.getType(), ids, ag1.getDescription(), ag1.lod); } - DescriptionLOD getLOD() { + public DescriptionLOD getLOD() { return lod; } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/events/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/events/FilteredEventsModel.java index 3b453930c1..0d19d1e28d 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/events/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/events/FilteredEventsModel.java @@ -259,4 +259,7 @@ public class FilteredEventsModel { // requestedLOD.set(zCrumb.getDescrLOD()); // } // } + public DescriptionLOD getDescriptionLOD() { + return requestedLOD.get(); + } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/events/type/RootEventType.java b/Core/src/org/sleuthkit/autopsy/timeline/events/type/RootEventType.java index 79716db2f7..dd6955fe37 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/events/type/RootEventType.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/events/type/RootEventType.java @@ -59,6 +59,9 @@ public class RootEventType implements EventType { private static class RootEventTypeHolder { private static final RootEventType INSTANCE = new RootEventType(); + + private RootEventTypeHolder() { + } } @Override @@ -76,8 +79,6 @@ public class RootEventType implements EventType { return Arrays.asList(BaseTypes.values()); } - - @Override public String getIconBase() { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/AbstractFilter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/AbstractFilter.java index 4e4f122f24..f9c945a997 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/filters/AbstractFilter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/AbstractFilter.java @@ -13,7 +13,6 @@ import javafx.beans.property.SimpleBooleanProperty; public abstract class AbstractFilter implements Filter { private final SimpleBooleanProperty active = new SimpleBooleanProperty(true); - private final SimpleBooleanProperty disabled = new SimpleBooleanProperty(false); @Override @@ -42,7 +41,7 @@ public abstract class AbstractFilter implements Filter { } @Override - public boolean isdisabled() { + public boolean isDisabled() { return disabled.get(); } @@ -50,4 +49,7 @@ public abstract class AbstractFilter implements Filter { public String getStringCheckBox() { return "[" + (isActive() ? "x" : " ") + "]"; } + + + } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/Filter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/Filter.java index df07d23ffa..4392fd68cd 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/filters/Filter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/Filter.java @@ -76,6 +76,5 @@ public interface Filter { SimpleBooleanProperty getDisabledProperty(); - boolean isdisabled(); - + boolean isDisabled(); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/HideKnownFilter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/HideKnownFilter.java index 86c36ce5fe..0742a91cbe 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/filters/HideKnownFilter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/HideKnownFilter.java @@ -37,7 +37,7 @@ public class HideKnownFilter extends AbstractFilter { public HideKnownFilter copyOf() { HideKnownFilter hideKnownFilter = new HideKnownFilter(); hideKnownFilter.setActive(isActive()); - hideKnownFilter.setDisabled(isdisabled()); + hideKnownFilter.setDisabled(isDisabled()); return hideKnownFilter; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/IntersectionFilter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/IntersectionFilter.java index b11b0f53b7..5af67c3483 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/filters/IntersectionFilter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/IntersectionFilter.java @@ -10,15 +10,15 @@ import javafx.collections.ObservableList; /** Intersection(And) filter */ public class IntersectionFilter extends CompoundFilter { - + public IntersectionFilter(ObservableList subFilters) { super(subFilters); } - + public IntersectionFilter() { super(FXCollections.observableArrayList()); } - + @Override public IntersectionFilter copyOf() { IntersectionFilter filter = new IntersectionFilter(FXCollections.observableArrayList( @@ -26,20 +26,22 @@ public class IntersectionFilter extends CompoundFilter { .map(Filter::copyOf) .collect(Collectors.toList()))); filter.setActive(isActive()); - filter.setDisabled(isdisabled()); + filter.setDisabled(isDisabled()); return filter; } - + @Override public String getDisplayName() { - return "Intersection"; + return "Intersection" + getSubFilters().stream() + .map(Filter::getDisplayName) + .collect(Collectors.joining(",", "[", "]")); } - + @Override public String getHTMLReportString() { return getSubFilters().stream().filter(Filter::isActive).map(Filter::getHTMLReportString).collect(Collectors.joining("
  • ", "
    • ", "
    ")); } - + @Override public boolean equals(Object obj) { if (obj == null) { @@ -49,11 +51,11 @@ public class IntersectionFilter extends CompoundFilter { return false; } final IntersectionFilter other = (IntersectionFilter) obj; - + if (isActive() != other.isActive()) { return false; } - + for (int i = 0; i < getSubFilters().size(); i++) { if (getSubFilters().get(i).equals(other.getSubFilters().get(i)) == false) { return false; @@ -61,7 +63,7 @@ public class IntersectionFilter extends CompoundFilter { } return true; } - + @Override public int hashCode() { int hash = 7; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/TextFilter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/TextFilter.java index 44a38c30e9..6d7db2ca0e 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/filters/TextFilter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/TextFilter.java @@ -56,7 +56,7 @@ public class TextFilter extends AbstractFilter { synchronized public TextFilter copyOf() { TextFilter textFilter = new TextFilter(getText()); textFilter.setActive(isActive()); - textFilter.setDisabled(isdisabled()); + textFilter.setDisabled(isDisabled()); return textFilter; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/TypeFilter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/TypeFilter.java index 0306e463d9..80f5d1edcb 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/filters/TypeFilter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/TypeFilter.java @@ -42,7 +42,7 @@ public class TypeFilter extends UnionFilter { private TypeFilter(EventType et, boolean recursive) { super(FXCollections.observableArrayList()); this.eventType = et; - + if (recursive) { // add subfilters for each subtype for (EventType subType : et.getSubTypes()) { this.getSubFilters().add(new TypeFilter(subType)); @@ -57,11 +57,11 @@ public class TypeFilter extends UnionFilter { public TypeFilter(EventType et) { this(et, true); } - + public EventType getEventType() { return eventType; } - + @Override public String getDisplayName() { return eventType == RootEventType.getInstance() ? "Event Type Filter" : eventType.getDisplayName(); @@ -76,21 +76,21 @@ public class TypeFilter extends UnionFilter { public Image getFXImage() { return eventType.getFXImage(); } - + @Override public TypeFilter copyOf() { //make a nonrecursive copy of this filter final TypeFilter typeFilter = new TypeFilter(eventType, false); typeFilter.setActive(isActive()); - typeFilter.setDisabled(isdisabled()); + typeFilter.setDisabled(isDisabled()); //add a copy of each subfilter this.getSubFilters().forEach((Filter t) -> { typeFilter.getSubFilters().add(t.copyOf()); }); - + return typeFilter; } - + @Override public String getHTMLReportString() { String string = getEventType().getDisplayName() + getStringCheckBox(); @@ -99,7 +99,7 @@ public class TypeFilter extends UnionFilter { } return string; } - + @Override public boolean equals(Object obj) { if (obj == null) { @@ -109,15 +109,15 @@ public class TypeFilter extends UnionFilter { return false; } final TypeFilter other = (TypeFilter) obj; - + if (isActive() != other.isActive()) { return false; } - + if (this.eventType != other.eventType) { return false; } - + for (int i = 0; i < getSubFilters().size(); i++) { if (getSubFilters().get(i).equals(other.getSubFilters().get(i)) == false) { return false; @@ -125,12 +125,12 @@ public class TypeFilter extends UnionFilter { } return true; } - + @Override public int hashCode() { int hash = 7; hash = 67 * hash + Objects.hashCode(this.eventType); return hash; } - + } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/images/minus-button.png b/Core/src/org/sleuthkit/autopsy/timeline/images/minus-button.png new file mode 100644 index 0000000000..108778b80e Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/timeline/images/minus-button.png differ diff --git a/Core/src/org/sleuthkit/autopsy/timeline/images/plus-button.png b/Core/src/org/sleuthkit/autopsy/timeline/images/plus-button.png new file mode 100644 index 0000000000..f6cced51a6 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/timeline/images/plus-button.png differ diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualization.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualization.java index aabac6e1bb..fad4f78949 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualization.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualization.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.timeline.ui; +import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import javafx.beans.InvalidationListener; @@ -102,7 +103,7 @@ public abstract class AbstractVisualization getSettingsNodes() { - return settingsNodes; + return Collections.unmodifiableList(settingsNodes); } /** @param value a value along this visualization's x axis diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.fxml b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.fxml index 7aa00b91b4..becc6fc797 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.fxml +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.fxml @@ -1,5 +1,6 @@ + @@ -14,10 +15,13 @@ - -