mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
fixed some things, and rolled back a lot of comments.
This commit is contained in:
parent
4909cbbd6e
commit
661016cb52
18
.gitignore
vendored
18
.gitignore
vendored
@ -46,13 +46,13 @@ genfiles.properties
|
||||
/branding/nbproject/*
|
||||
!/branding/nbproject/project.xml
|
||||
!/branding/nbproject/project.properties
|
||||
/Testing/script/input/
|
||||
/Testing/script/output/
|
||||
/Testing/script/gold/
|
||||
/Testing/script/ScriptLog.txt
|
||||
/Testing/build/
|
||||
/Testing/dist/
|
||||
/Testing/nbproject/*
|
||||
/Test/input/
|
||||
/Test/input/*
|
||||
/Test/output/*
|
||||
/Test/script/ScriptLog.txt
|
||||
/Test/build/
|
||||
/Test/dist/
|
||||
/Test/nbproject/*
|
||||
!/Testing/nbproject/project.xml
|
||||
!/Testing/nbproject/project.properties
|
||||
*~
|
||||
@ -64,5 +64,5 @@ genfiles.properties
|
||||
hs_err_pid*.log
|
||||
Core/src/org/sleuthkit/autopsy/casemodule/docs/QuickStart.html
|
||||
Core/src/org/sleuthkit/autopsy/casemodule/docs/screenshot.png
|
||||
/Testing/script/myconfig.xml
|
||||
/Testing/script/*/*.xml
|
||||
/Test/script/myconfig.xml
|
||||
/Test/script/*/*.xml
|
||||
|
@ -1,4 +1,4 @@
|
||||
Last Updated: Sept 28, 2012
|
||||
Last Updated: March 23, 2013
|
||||
|
||||
This file outlines what it takes to build Autopsy from source.
|
||||
|
||||
@ -14,6 +14,11 @@ STEPS:
|
||||
1a) Download and install 32-bit version of JDK version 1.7 (32-bit is currently
|
||||
needed even if you have a 64-bit system).
|
||||
|
||||
Autopsy has been used and tested with Oracle JavaSE and the included JavaFX support
|
||||
(http://www.oracle.com/technetwork/java/javase/downloads/index.html).
|
||||
|
||||
OpenJDK and OpenJFX might work, but they are not fully tested with Autopsy.
|
||||
|
||||
1b) Ensure that JDK_HOME is set to the root JDK directory.
|
||||
|
||||
1c) (optional) Download and install Netbeans IDE (http://netbeans.org/)
|
||||
@ -28,7 +33,7 @@ need to set JRE_HOME to the root JRE directory.
|
||||
|
||||
|
||||
2) Get Sleuth Kit Setup
|
||||
2a) Download and build the release version of Libewf2 (20120304 or
|
||||
2a) Download and build the release version of Libewf2 (20130119 or
|
||||
later). All you need is the dll file. Note that you will get a
|
||||
launching error if you use libewf 1.
|
||||
- http://sourceforge.net/projects/libewf/
|
||||
|
@ -116,6 +116,8 @@ public class Case {
|
||||
private static Case currentCase = null;
|
||||
private Services services;
|
||||
private static final Logger logger = Logger.getLogger(Case.class.getName());
|
||||
static final String CASE_EXTENSION = "aut";
|
||||
static final String CASE_DOT_EXTENSION = "." + CASE_EXTENSION;
|
||||
|
||||
/**
|
||||
* Constructor for the Case class
|
||||
@ -202,7 +204,7 @@ public class Case {
|
||||
static void create(String caseDir, String caseName, String caseNumber, String examiner) throws CaseActionException {
|
||||
logger.log(Level.INFO, "Creating new case.\ncaseDir: {0}\ncaseName: {1}", new Object[]{caseDir, caseName});
|
||||
|
||||
String configFilePath = caseDir + File.separator + caseName + ".aut";
|
||||
String configFilePath = caseDir + File.separator + caseName + CASE_DOT_EXTENSION;
|
||||
|
||||
XMLCaseManagement xmlcm = new XMLCaseManagement();
|
||||
xmlcm.create(caseDir, caseName, examiner, caseNumber); // create a new XML config file
|
||||
@ -260,7 +262,12 @@ public class Case {
|
||||
// close the previous case if there's any
|
||||
CaseCloseAction closeCase = SystemAction.get(CaseCloseAction.class);
|
||||
closeCase.actionPerformed(null);
|
||||
throw new CaseActionException("Error opening the case", ex);
|
||||
if (!configFilePath.endsWith(CASE_DOT_EXTENSION)) {
|
||||
throw new CaseActionException("Check that you selected the correct case file (usually with "
|
||||
+ CASE_DOT_EXTENSION + " extension)", ex);
|
||||
} else {
|
||||
throw new CaseActionException("Error opening the case", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -562,7 +569,6 @@ public class Case {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get absolute module output directory path where modules should save their
|
||||
* permanent data The directory is a subdirectory of this case dir.
|
||||
@ -916,7 +922,7 @@ public class Case {
|
||||
|
||||
Frame f = WindowManager.getDefault().getMainWindow();
|
||||
f.setTitle(Case.getAppName()); // set the window name to just application name
|
||||
|
||||
|
||||
//try to force gc to happen
|
||||
System.gc();
|
||||
System.gc();
|
||||
@ -924,7 +930,7 @@ public class Case {
|
||||
|
||||
//log memory usage after case changed
|
||||
logger.log(Level.INFO, PlatformUtil.getAllMemUsageInfo());
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
import java.awt.Component;
|
||||
@ -27,6 +26,8 @@ import java.util.Collections;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
@ -36,33 +37,35 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
*/
|
||||
@ServiceProvider(service = CaseOpenAction.class)
|
||||
public final class CaseOpenAction implements ActionListener {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(CaseOpenAction.class.getName());
|
||||
private static final String PROP_BASECASE = "LBL_BaseCase_PATH";
|
||||
private final JFileChooser fc = new JFileChooser();
|
||||
private FileFilter autFilter;
|
||||
|
||||
JFileChooser fc = new JFileChooser();
|
||||
GeneralFilter autFilter = new GeneralFilter(Collections.<String>singletonList(".aut"), "AUTOPSY File (*.aut)");
|
||||
|
||||
/** The constructor */
|
||||
/**
|
||||
* The constructor
|
||||
*/
|
||||
public CaseOpenAction() {
|
||||
autFilter = new FileNameExtensionFilter(org.sleuthkit.autopsy.coreutils.Version.getName()
|
||||
+ " Case File ( " + Case.CASE_DOT_EXTENSION + ")",
|
||||
Case.CASE_EXTENSION);
|
||||
fc.setDragEnabled(false);
|
||||
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
||||
fc.setMultiSelectionEnabled(false);
|
||||
//fc.setAcceptAllFileFilterUsed(false);
|
||||
fc.addChoosableFileFilter(autFilter);
|
||||
//fc.addChoosableFileFilter(fc.getAcceptAllFileFilter());
|
||||
try{
|
||||
if(ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, PROP_BASECASE) != null)
|
||||
fc.setCurrentDirectory(new File(ModuleSettings.getConfigSetting("Case", PROP_BASECASE)));
|
||||
}
|
||||
catch(Exception e){
|
||||
|
||||
fc.setFileFilter(autFilter);
|
||||
try {
|
||||
if (ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, PROP_BASECASE) != null) {
|
||||
fc.setCurrentDirectory(new File(ModuleSettings.getConfigSetting("Case", PROP_BASECASE)));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop-up the File Chooser to open the existing case (.aut file)
|
||||
*
|
||||
* @param e the action event
|
||||
*
|
||||
* @param e the action event
|
||||
*/
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
@ -70,6 +73,7 @@ public final class CaseOpenAction implements ActionListener {
|
||||
|
||||
|
||||
int retval = fc.showOpenDialog((Component) e.getSource());
|
||||
|
||||
if (retval == JFileChooser.APPROVE_OPTION) {
|
||||
String path = fc.getSelectedFile().getPath();
|
||||
String dirPath = fc.getSelectedFile().getParent();
|
||||
@ -89,11 +93,10 @@ public final class CaseOpenAction implements ActionListener {
|
||||
try {
|
||||
Case.open(path); // open the case
|
||||
} catch (CaseActionException ex) {
|
||||
JOptionPane.showMessageDialog(null, "Error: could not open the case in folder " + path
|
||||
+ " due to unexpected error"
|
||||
, "Error", JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(null, "Error: could not open the case in folder " + path
|
||||
+ ": " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
logger.log(Level.WARNING, "Error opening case in folder " + path, ex);
|
||||
|
||||
|
||||
StartupWindow.getInstance().open();
|
||||
}
|
||||
}
|
||||
|
@ -28,10 +28,11 @@ import javax.swing.filechooser.FileFilter;
|
||||
*/
|
||||
public class GeneralFilter extends FileFilter{
|
||||
|
||||
List<String> extensions;
|
||||
String desc;
|
||||
private List<String> extensions;
|
||||
private String desc;
|
||||
|
||||
public GeneralFilter(List<String> ext, String desc){
|
||||
super();
|
||||
this.extensions = ext;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener{
|
||||
//fc.setSelectedFile(new File("C:\\Program Files\\"));
|
||||
//disableTextField(fc); // disable all the text field on the file chooser
|
||||
|
||||
int returnValue = fc.showSaveDialog((Component)evt.getSource());
|
||||
int returnValue = fc.showDialog((Component)evt.getSource(), "Select");
|
||||
if(returnValue == JFileChooser.APPROVE_OPTION){
|
||||
String path = fc.getSelectedFile().getPath();
|
||||
caseParentDirTextField.setText(path); // put the path to the textfield
|
||||
|
@ -101,6 +101,14 @@ public class Installer extends ModuleInstall {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if JavaFx initialized
|
||||
* @return false if java fx not initialized (classes coult not load), true if initialized
|
||||
*/
|
||||
public boolean isJavaFxInited() {
|
||||
return this.javaFxInit;
|
||||
}
|
||||
|
||||
private void initJavaFx() {
|
||||
//initialize java fx if exists
|
||||
try {
|
||||
@ -118,7 +126,7 @@ public class Installer extends ModuleInstall {
|
||||
final String details = " Some features will not be available. "
|
||||
+ " Check that you have the right JRE installed (Sun JRE > 1.7.10). ";
|
||||
logger.log(Level.SEVERE, msg
|
||||
+ details);
|
||||
+ details, e);
|
||||
|
||||
WindowManager.getDefault().invokeWhenUIReady(new Runnable() {
|
||||
@Override
|
||||
|
@ -76,8 +76,6 @@ DataContentViewerArtifact.selectAllMenuItem.text=Select All
|
||||
DataContentViewerArtifact.pageLabel.text=Result:
|
||||
|
||||
AdvancedConfigurationDialog.applyButton.text=OK
|
||||
DataContentViewerMedia.pauseButton.text=\u25ba
|
||||
DataContentViewerMedia.progressLabel.text=
|
||||
DataContentViewerString.goToPageLabel.text=Go to Page:
|
||||
DataContentViewerString.goToPageTextField.text=
|
||||
DataContentViewerHex.goToPageTextField.text=
|
||||
@ -104,3 +102,7 @@ AdvancedConfigurationDialog.cancelButton.text=Cancel
|
||||
DataResultPanel.directoryTablePath.text=directoryPath
|
||||
DataResultPanel.numberMatchLabel.text=0
|
||||
DataResultPanel.matchLabel.text=Results
|
||||
MediaViewVideoPanel.pauseButton.text=\u25ba
|
||||
MediaViewVideoPanel.progressLabel.text=00:00
|
||||
DataContentViewerMedia.AccessibleContext.accessibleDescription=
|
||||
MediaViewVideoPanel.infoLabel.text=info
|
||||
|
@ -11,25 +11,10 @@
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="dataContentPanel" alignment="0" pref="585" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="dataContentPanel" alignment="0" pref="435" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
|
||||
<Property name="axis" type="int" value="1"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="org.sleuthkit.autopsy.corecomponents.DataContentPanel" name="dataContentPanel">
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new DataContentPanel(true);"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -47,6 +47,8 @@ public final class DataContentTopComponent extends TopComponent implements DataC
|
||||
private static DataContentTopComponent defaultInstance;
|
||||
// set to true if this is the TC that always stays open and is the default place to display content
|
||||
private boolean isDefault;
|
||||
// the content panel holding tabs with content viewers
|
||||
private final DataContentPanel dataContentPanel;
|
||||
|
||||
// contains a list of the undocked TCs
|
||||
private static ArrayList<DataContentTopComponent> newWindowList = new ArrayList<DataContentTopComponent>();
|
||||
@ -60,6 +62,10 @@ public final class DataContentTopComponent extends TopComponent implements DataC
|
||||
setToolTipText(TOOLTIP_TEXT);
|
||||
|
||||
this.isDefault = isDefault;
|
||||
|
||||
dataContentPanel = new DataContentPanel(isDefault);
|
||||
add(dataContentPanel);
|
||||
|
||||
putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.valueOf(isDefault)); // prevent option to close compoment in GUI
|
||||
logger.log(Level.INFO, "Created DataContentTopComponent instance: " + this);
|
||||
}
|
||||
@ -90,21 +96,9 @@ public final class DataContentTopComponent extends TopComponent implements DataC
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
dataContentPanel = new DataContentPanel(true);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(dataContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 585, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(dataContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 435, Short.MAX_VALUE)
|
||||
);
|
||||
setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS));
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private org.sleuthkit.autopsy.corecomponents.DataContentPanel dataContentPanel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<AccessibilityProperties>
|
||||
<Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerMedia.AccessibleContext.accessibleDescription" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</AccessibilityProperties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
@ -13,80 +18,5 @@
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="videoPanel" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="pauseButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="progressSlider" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="progressLabel" min="-2" pref="104" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="videoPanel" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" max="-2" attributes="0">
|
||||
<Component id="pauseButton" max="32767" attributes="0"/>
|
||||
<Component id="progressSlider" max="-2" attributes="0"/>
|
||||
<Component id="progressLabel" alignment="1" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="pauseButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerMedia.pauseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[45, 23]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[45, 23]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[45, 23]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pauseButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JPanel" name="videoPanel">
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="242" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
<Component class="javax.swing.JSlider" name="progressSlider">
|
||||
<Properties>
|
||||
<Property name="value" type="int" value="0"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="progressLabel">
|
||||
<Properties>
|
||||
<Property name="horizontalAlignment" type="int" value="4"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerMedia.progressLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignCardLayout"/>
|
||||
</Form>
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011 Basis Technology Corp.
|
||||
* Copyright 2011-2013 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,95 +18,62 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.corecomponents;
|
||||
|
||||
import com.sun.javafx.application.PlatformImpl;
|
||||
import java.awt.CardLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import javafx.application.Platform;
|
||||
import javafx.embed.swing.JFXPanel;
|
||||
import javafx.embed.swing.SwingFXUtils;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javax.imageio.ImageIO;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import org.gstreamer.*;
|
||||
import org.gstreamer.elements.PlayBin2;
|
||||
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.nodes.Node;
|
||||
import org.openide.util.Cancellable;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.openide.util.lookup.ServiceProviders;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.ReadContentInputStream;
|
||||
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
|
||||
|
||||
/**
|
||||
* Media content viewer for videos, sounds and images. Using gstreamer.
|
||||
* Media content viewer for videos, sounds and images.
|
||||
*/
|
||||
@ServiceProviders(value = {
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 5),
|
||||
@ServiceProvider(service = FrameCapture.class)
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 5)
|
||||
})
|
||||
public class DataContentViewerMedia extends javax.swing.JPanel implements DataContentViewer, FrameCapture {
|
||||
public class DataContentViewerMedia extends javax.swing.JPanel implements DataContentViewer {
|
||||
|
||||
private String[] IMAGES; // use javafx supported
|
||||
private static final String[] VIDEOS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg"};
|
||||
private static final String[] VIDEOS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"};
|
||||
private static final String[] AUDIOS = new String[]{".mp3", ".wav", ".wma"};
|
||||
private static final int NUM_FRAMES = 12;
|
||||
private static final long MIN_FRAME_INTERVAL_MILLIS = 500;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DataContentViewerMedia.class.getName());
|
||||
private VideoComponent videoComponent;
|
||||
private PlayBin2 playbin2;
|
||||
private AbstractFile currentFile;
|
||||
private long durationMillis = 0;
|
||||
private boolean autoTracking = false; // true if the slider is moving automatically
|
||||
private final Object playbinLock = new Object(); // lock for synchronization of playbin2 player
|
||||
private VideoProgressWorker videoProgressWorker;
|
||||
private int totalHours, totalMinutes, totalSeconds;
|
||||
private BufferedImage currentImage = null;
|
||||
private boolean gstInited = false;
|
||||
|
||||
private AbstractFile lastFile;
|
||||
private boolean inImageMode; //keeps track if already in image mode to minimize UI setup
|
||||
//UI
|
||||
private final MediaViewVideoPanel videoPanel;
|
||||
private final MediaViewImagePanel imagePanel;
|
||||
private boolean videoPanelInited;
|
||||
private boolean imagePanelInited;
|
||||
|
||||
private static final String IMAGE_VIEWER_LAYER = "IMAGE";
|
||||
private static final String VIDEO_VIEWER_LAYER = "VIDEO";
|
||||
|
||||
/**
|
||||
* Creates new form DataContentViewerVideo
|
||||
*/
|
||||
public DataContentViewerMedia() {
|
||||
|
||||
initComponents();
|
||||
|
||||
videoPanel = new MediaViewVideoPanel();
|
||||
imagePanel = new MediaViewImagePanel();
|
||||
videoPanelInited = videoPanel.isInited();
|
||||
imagePanelInited = imagePanel.isInited();
|
||||
|
||||
customizeComponents();
|
||||
logger.log(Level.INFO, "Created MediaView instance: " + this);
|
||||
}
|
||||
|
||||
private void customizeComponents() {
|
||||
inImageMode = false;
|
||||
|
||||
logger.log(Level.INFO, "Supported image formats by javafx image viewer: ");
|
||||
//initialize supported image types
|
||||
//TODO use mime-types instead once we have support
|
||||
@ -118,47 +85,11 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
||||
IMAGES[i] = "." + suffix;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.log(Level.INFO, "Initializing gstreamer for video/audio viewing");
|
||||
Gst.init();
|
||||
gstInited = true;
|
||||
} catch (GstException e) {
|
||||
gstInited = false;
|
||||
logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and frame extraction capabilities", e);
|
||||
MessageNotifyUtil.Notify.error("Error initializing gstreamer for audio/video viewing and frame extraction capabilities. "
|
||||
+ " Video and audio viewing will be disabled. ",
|
||||
e.getMessage());
|
||||
return;
|
||||
} catch (UnsatisfiedLinkError | NoClassDefFoundError | Exception e) {
|
||||
gstInited = false;
|
||||
logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and extraction capabilities", e);
|
||||
MessageNotifyUtil.Notify.error("Error initializing gstreamer for audio/video viewing frame extraction capabilities. "
|
||||
+ " Video and audio viewing will be disabled. ",
|
||||
e.getMessage());
|
||||
return;
|
||||
}
|
||||
add(imagePanel, IMAGE_VIEWER_LAYER);
|
||||
add(videoPanel, VIDEO_VIEWER_LAYER);
|
||||
|
||||
switchPanels(false);
|
||||
|
||||
|
||||
progressSlider.setEnabled(false); // disable slider; enable after user plays vid
|
||||
progressSlider.addChangeListener(new ChangeListener() {
|
||||
/**
|
||||
* Should always try to synchronize any call to
|
||||
* progressSlider.setValue() to avoid a different thread changing
|
||||
* playbin while stateChanged() is processing
|
||||
*/
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
int time = progressSlider.getValue();
|
||||
synchronized (playbinLock) {
|
||||
if (playbin2 != null && !autoTracking) {
|
||||
State orig = playbin2.getState();
|
||||
playbin2.pause();
|
||||
playbin2.seek(ClockTime.fromMillis(time));
|
||||
playbin2.setState(orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -170,90 +101,17 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
pauseButton = new javax.swing.JButton();
|
||||
videoPanel = new javax.swing.JPanel();
|
||||
progressSlider = new javax.swing.JSlider();
|
||||
progressLabel = new javax.swing.JLabel();
|
||||
|
||||
pauseButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerMedia.class, "DataContentViewerMedia.pauseButton.text")); // NOI18N
|
||||
pauseButton.setMaximumSize(new java.awt.Dimension(45, 23));
|
||||
pauseButton.setMinimumSize(new java.awt.Dimension(45, 23));
|
||||
pauseButton.setPreferredSize(new java.awt.Dimension(45, 23));
|
||||
pauseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
pauseButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout videoPanelLayout = new javax.swing.GroupLayout(videoPanel);
|
||||
videoPanel.setLayout(videoPanelLayout);
|
||||
videoPanelLayout.setHorizontalGroup(
|
||||
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 0, Short.MAX_VALUE)
|
||||
);
|
||||
videoPanelLayout.setVerticalGroup(
|
||||
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 242, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
progressSlider.setValue(0);
|
||||
|
||||
progressLabel.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
|
||||
progressLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerMedia.class, "DataContentViewerMedia.progressLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(pauseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(progressLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 104, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
|
||||
.addComponent(pauseButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(progressLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
|
||||
);
|
||||
setLayout(new java.awt.CardLayout());
|
||||
getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(DataContentViewerMedia.class, "DataContentViewerMedia.AccessibleContext.accessibleDescription")); // NOI18N
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseButtonActionPerformed
|
||||
synchronized (playbinLock) {
|
||||
State state = playbin2.getState();
|
||||
if (state.equals(State.PLAYING)) {
|
||||
playbin2.pause();
|
||||
pauseButton.setText("►");
|
||||
playbin2.setState(State.PAUSED);
|
||||
} else if (state.equals(State.PAUSED)) {
|
||||
playbin2.play();
|
||||
pauseButton.setText("||");
|
||||
playbin2.setState(State.PLAYING);
|
||||
} else if (state.equals(State.READY)) {
|
||||
ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile));
|
||||
em.execute();
|
||||
em.getExtractedBytes();
|
||||
}
|
||||
}
|
||||
}//GEN-LAST:event_pauseButtonActionPerformed
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton pauseButton;
|
||||
private javax.swing.JLabel progressLabel;
|
||||
private javax.swing.JSlider progressSlider;
|
||||
private javax.swing.JPanel videoPanel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
@Override
|
||||
public void setNode(Node selectedNode) {
|
||||
|
||||
|
||||
if (selectedNode == null) {
|
||||
videoPanel.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -267,214 +125,34 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
||||
} else {
|
||||
lastFile = file;
|
||||
}
|
||||
|
||||
reset();
|
||||
setComponentsVisibility(false);
|
||||
|
||||
// get rid of any existing videoProgressWorker thread
|
||||
if (videoProgressWorker != null) {
|
||||
videoProgressWorker.cancel(true);
|
||||
videoProgressWorker = null;
|
||||
}
|
||||
|
||||
|
||||
currentFile = file;
|
||||
if (containsExt(file.getName(), IMAGES)) {
|
||||
showImageFx(file);
|
||||
} else if (gstInited
|
||||
|
||||
videoPanel.reset();
|
||||
|
||||
final Dimension dims = DataContentViewerMedia.this.getSize();
|
||||
|
||||
if (imagePanelInited && containsExt(file.getName(), IMAGES)) {
|
||||
imagePanel.showImageFx(file, dims);
|
||||
this.switchPanels(false);
|
||||
} else if (videoPanelInited
|
||||
&& (containsExt(file.getName(), VIDEOS) || containsExt(file.getName(), AUDIOS))) {
|
||||
inImageMode = false;
|
||||
setupVideo(file);
|
||||
|
||||
videoPanel.setupVideo(file, dims);
|
||||
this.switchPanels(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize vars and display the image on the panel.
|
||||
* switch to visible video or image panel
|
||||
*
|
||||
* @param file
|
||||
* @param showVideo true if video panel, false if image panel
|
||||
*/
|
||||
private void showImageFx(final AbstractFile file) {
|
||||
final String fileName = file.getName();
|
||||
|
||||
// load the image
|
||||
PlatformImpl.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!Case.isCaseOpen()) {
|
||||
//handle in-between condition when case is being closed
|
||||
//and an image was previously selected
|
||||
return;
|
||||
}
|
||||
|
||||
Dimension dims = DataContentViewerMedia.this.getSize();
|
||||
|
||||
final InputStream inputStream = new ReadContentInputStream(file);
|
||||
|
||||
final Image fxImage;
|
||||
try {
|
||||
//original input stream
|
||||
BufferedImage bi = ImageIO.read(inputStream);
|
||||
//scale image using Scalr
|
||||
BufferedImage biScaled = ScalrWrapper.resizeHighQuality(bi, (int) dims.getWidth(), (int) dims.getHeight());
|
||||
//convert from awt imageto fx image
|
||||
fxImage = SwingFXUtils.toFXImage(biScaled, null);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Could not load image file into media view: " + fileName, ex);
|
||||
return;
|
||||
} catch (OutOfMemoryError ex) {
|
||||
logger.log(Level.WARNING, "Could not load image file into media view (too large): " + fileName, ex);
|
||||
MessageNotifyUtil.Notify.warn("Could not load image file (too large): " + file.getName(), ex.getMessage());
|
||||
return;
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Could not close input stream after loading image in media view: " + fileName, ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (fxImage == null) {
|
||||
logger.log(Level.WARNING, "Could not load image file into media view: " + fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
// simple displays ImageView the image as is
|
||||
ImageView fxImageView = new ImageView();
|
||||
fxImageView.setImage(fxImage);
|
||||
// resizes the image to have width of 100 while preserving the ratio and using
|
||||
// higher quality filtering method; this ImageView is also cached to
|
||||
// improve performance
|
||||
fxImageView.setPreserveRatio(true);
|
||||
fxImageView.setSmooth(true);
|
||||
fxImageView.setCache(true);
|
||||
fxImageView.setFitWidth(dims.getWidth());
|
||||
fxImageView.setFitHeight(dims.getHeight());
|
||||
|
||||
Group fxRoot = new Group();
|
||||
|
||||
//Scene fxScene = new Scene(fxRoot, dims.getWidth(), dims.getHeight(), javafx.scene.paint.Color.BLACK);
|
||||
Scene fxScene = new Scene(fxRoot, javafx.scene.paint.Color.BLACK);
|
||||
fxRoot.getChildren().add(fxImageView);
|
||||
|
||||
if (inImageMode) {
|
||||
final JFXPanel fxPanel = (JFXPanel) videoPanel.getComponent(0);
|
||||
fxPanel.setScene(fxScene);
|
||||
videoPanel.setVisible(true);
|
||||
} else {
|
||||
inImageMode = true;
|
||||
final JFXPanel fxPanel = new JFXPanel();
|
||||
fxPanel.setScene(fxScene);
|
||||
|
||||
//when done, join with the swing panel
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
//remove video panels and recreate image view panel
|
||||
//TODO use swing layered pane to switch between different modes
|
||||
videoPanel.removeAll();
|
||||
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
||||
videoPanel.add(fxPanel);
|
||||
videoPanel.setVisible(true);
|
||||
|
||||
if (fxImage.isError()) {
|
||||
logger.log(Level.WARNING, "Could not load image file into media view: " + fileName);
|
||||
//MessageNotifyUtil.Message.warn("Could not load image file: " + file.getName());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize vars and display the image on the panel.
|
||||
*
|
||||
* @param file
|
||||
* @deprecated using javafx for image display
|
||||
*/
|
||||
@Deprecated
|
||||
private void showImageGst(AbstractFile file) {
|
||||
if (!gstInited) {
|
||||
return;
|
||||
private void switchPanels(boolean showVideo) {
|
||||
CardLayout layout = (CardLayout)this.getLayout();
|
||||
if (showVideo) {
|
||||
layout.show(this, VIDEO_VIEWER_LAYER);
|
||||
} else {
|
||||
layout.show(this, IMAGE_VIEWER_LAYER);
|
||||
}
|
||||
java.io.File ioFile = getJFile(file);
|
||||
if (!ioFile.exists()) {
|
||||
try {
|
||||
ContentUtils.writeToFile(file, ioFile);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Error buffering file", ex);
|
||||
}
|
||||
}
|
||||
|
||||
videoComponent = new VideoComponent();
|
||||
synchronized (playbinLock) {
|
||||
if (playbin2 != null) {
|
||||
playbin2.dispose();
|
||||
}
|
||||
playbin2 = new PlayBin2("ImageViewer");
|
||||
playbin2.setVideoSink(videoComponent.getElement());
|
||||
}
|
||||
|
||||
videoPanel.removeAll();
|
||||
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
||||
videoPanel.add(videoComponent);
|
||||
videoPanel.revalidate();
|
||||
videoPanel.repaint();
|
||||
|
||||
synchronized (playbinLock) {
|
||||
playbin2.setInputFile(ioFile);
|
||||
playbin2.play();
|
||||
}
|
||||
videoPanel.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all the necessary vars to play a video/audio file.
|
||||
*
|
||||
* @param file the file to play
|
||||
*/
|
||||
private void setupVideo(AbstractFile file) {
|
||||
java.io.File ioFile = getJFile(file);
|
||||
|
||||
pauseButton.setText("►");
|
||||
progressSlider.setValue(0);
|
||||
|
||||
videoComponent = new VideoComponent();
|
||||
synchronized (playbinLock) {
|
||||
if (playbin2 != null) {
|
||||
playbin2.dispose();
|
||||
}
|
||||
playbin2 = new PlayBin2("VideoPlayer");
|
||||
playbin2.setVideoSink(videoComponent.getElement());
|
||||
}
|
||||
|
||||
videoPanel.removeAll();
|
||||
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
||||
videoPanel.add(videoComponent);
|
||||
videoPanel.revalidate();
|
||||
videoPanel.repaint();
|
||||
|
||||
synchronized (playbinLock) {
|
||||
playbin2.setInputFile(ioFile);
|
||||
playbin2.setState(State.READY);
|
||||
}
|
||||
setComponentsVisibility(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* To set the visibility of specific components in this class.
|
||||
*
|
||||
* @param isVisible whether to show or hide the specific components
|
||||
*/
|
||||
private void setComponentsVisibility(boolean isVisible) {
|
||||
pauseButton.setVisible(isVisible);
|
||||
progressLabel.setVisible(isVisible);
|
||||
progressSlider.setVisible(isVisible);
|
||||
videoPanel.setVisible(isVisible);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -484,7 +162,7 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
||||
|
||||
@Override
|
||||
public String getToolTip() {
|
||||
return "Displays supported multimedia files";
|
||||
return "Displays supported multimedia files (images, videos, audio)";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -503,50 +181,20 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
||||
// because we already reset on each selected node
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
// reset the progress label text on the event dispatch thread
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
progressLabel.setText("");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (!gstInited) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (playbinLock) {
|
||||
if (playbin2 != null) {
|
||||
if (playbin2.isPlaying()) {
|
||||
playbin2.stop();
|
||||
}
|
||||
playbin2.setState(State.NULL);
|
||||
if (playbin2.getState().equals(State.NULL)) {
|
||||
playbin2.dispose();
|
||||
}
|
||||
playbin2 = null;
|
||||
}
|
||||
videoComponent = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isSupported(Node node) {
|
||||
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
|
||||
if (file == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//try displaying deleted files if we can read them
|
||||
//if (file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC)) {
|
||||
// return false;
|
||||
//}
|
||||
|
||||
if (file.getSize() == 0) {
|
||||
return false;
|
||||
@ -554,15 +202,13 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
||||
|
||||
String name = file.getName().toLowerCase();
|
||||
|
||||
boolean deleted = file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC);
|
||||
|
||||
if (containsExt(name, IMAGES)) {
|
||||
if (imagePanelInited && containsExt(name, IMAGES)) {
|
||||
return true;
|
||||
} //for gstreamer formats, check if initialized first, then
|
||||
//support audio formats, and video formats if undeleted file
|
||||
else if (gstInited
|
||||
//support audio formats, and video formats
|
||||
else if (videoPanelInited && videoPanel.isInited()
|
||||
&& (containsExt(name, AUDIOS)
|
||||
|| (!deleted && containsExt(name, VIDEOS)))) {
|
||||
|| (containsExt(name, VIDEOS)))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -572,7 +218,19 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
||||
@Override
|
||||
public int isPreferred(Node node, boolean isSupported) {
|
||||
if (isSupported) {
|
||||
return 7;
|
||||
//special case, check if deleted video, then do not make it preferred
|
||||
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
|
||||
if (file == null) {
|
||||
return 0;
|
||||
}
|
||||
String name = file.getName().toLowerCase();
|
||||
|
||||
boolean deleted = file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC);
|
||||
if (containsExt(name, VIDEOS) && deleted) {
|
||||
return 0;
|
||||
} else {
|
||||
return 7;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
@ -586,274 +244,13 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
||||
}
|
||||
return Arrays.asList(exts).contains(ext);
|
||||
}
|
||||
|
||||
private java.io.File getJFile(AbstractFile file) {
|
||||
// Get the temp folder path of the case
|
||||
String tempPath = Case.getCurrentCase().getTempDirectory();
|
||||
String name = file.getName();
|
||||
int extStart = name.lastIndexOf(".");
|
||||
String ext = "";
|
||||
if (extStart != -1) {
|
||||
ext = name.substring(extStart, name.length()).toLowerCase();
|
||||
}
|
||||
tempPath = tempPath + java.io.File.separator + file.getId() + ext;
|
||||
|
||||
java.io.File tempFile = new java.io.File(tempPath);
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VideoFrame> captureFrames(java.io.File file, int numFrames) {
|
||||
|
||||
List<VideoFrame> frames = new ArrayList<>();
|
||||
|
||||
if (!gstInited) {
|
||||
return frames;
|
||||
}
|
||||
|
||||
RGBDataSink.Listener listener1 = new RGBDataSink.Listener() {
|
||||
@Override
|
||||
public void rgbFrame(boolean bln, int w, int h, IntBuffer rgbPixels) {
|
||||
BufferedImage curImage = new BufferedImage(w, h,
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
curImage.setRGB(0, 0, w, h, rgbPixels.array(), 0, w);
|
||||
currentImage = curImage;
|
||||
}
|
||||
};
|
||||
|
||||
// set up a PlayBin2 object
|
||||
RGBDataSink videoSink = new RGBDataSink("rgb", listener1);
|
||||
PlayBin2 playbin = new PlayBin2("VideoFrameCapture");
|
||||
playbin.setInputFile(file);
|
||||
playbin.setVideoSink(videoSink);
|
||||
|
||||
// this is necessary to get a valid duration value
|
||||
playbin.play();
|
||||
playbin.pause();
|
||||
playbin.getState();
|
||||
|
||||
// get the duration of the video
|
||||
TimeUnit unit = TimeUnit.MILLISECONDS;
|
||||
long myDurationMillis = playbin.queryDuration(unit);
|
||||
if (myDurationMillis <= 0) {
|
||||
return frames;
|
||||
}
|
||||
|
||||
// create a list of timestamps at which to get frames
|
||||
int numFramesToGet = numFrames;
|
||||
long frameInterval = myDurationMillis / numFrames;
|
||||
if (frameInterval < MIN_FRAME_INTERVAL_MILLIS) {
|
||||
numFramesToGet = 1;
|
||||
}
|
||||
|
||||
// for each timeStamp, grap a frame
|
||||
for (int i = 0; i < numFramesToGet; ++i) {
|
||||
long timeStamp = i * frameInterval;
|
||||
|
||||
playbin.pause();
|
||||
playbin.getState();
|
||||
|
||||
currentImage = null;
|
||||
if (!playbin.seek(timeStamp, unit)) {
|
||||
logger.log(Level.INFO, "There was a problem seeking to " + timeStamp + " " + unit.name().toLowerCase());
|
||||
}
|
||||
playbin.play();
|
||||
|
||||
while (currentImage == null) {
|
||||
System.out.flush(); // not sure why this is needed
|
||||
}
|
||||
|
||||
playbin.stop();
|
||||
|
||||
frames.add(new VideoFrame(currentImage, timeStamp));
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
/* Thread that extracts and plays a file */
|
||||
private class ExtractMedia extends SwingWorker<Object, Void> {
|
||||
|
||||
private ProgressHandle progress;
|
||||
boolean success = false;
|
||||
private AbstractFile sFile;
|
||||
private java.io.File jFile;
|
||||
private String duration;
|
||||
private String position;
|
||||
private long extractedBytes;
|
||||
|
||||
ExtractMedia(org.sleuthkit.datamodel.AbstractFile sFile, java.io.File jFile) {
|
||||
this.sFile = sFile;
|
||||
this.jFile = jFile;
|
||||
}
|
||||
|
||||
public long getExtractedBytes() {
|
||||
return extractedBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
success = false;
|
||||
progress = ProgressHandleFactory.createHandle("Buffering " + sFile.getName(), new Cancellable() {
|
||||
@Override
|
||||
public boolean cancel() {
|
||||
return ExtractMedia.this.cancel(true);
|
||||
}
|
||||
});
|
||||
progressLabel.setText("Buffering... ");
|
||||
progress.start();
|
||||
progress.switchToDeterminate(100);
|
||||
try {
|
||||
extractedBytes = ContentUtils.writeToFile(sFile, jFile, progress, this, true);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Error buffering file", ex);
|
||||
}
|
||||
success = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
/* clean up or start the worker threads */
|
||||
@Override
|
||||
protected void done() {
|
||||
try {
|
||||
super.get(); //block and get all exceptions thrown while doInBackground()
|
||||
} catch (CancellationException ex) {
|
||||
logger.log(Level.INFO, "Media buffering was canceled.");
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.INFO, "Media buffering was interrupted.");
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Fatal error during media buffering.", ex);
|
||||
} finally {
|
||||
progress.finish();
|
||||
if (!this.isCancelled()) {
|
||||
play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void play() {
|
||||
if (jFile == null || !jFile.exists()) {
|
||||
progressLabel.setText("Error buffering file");
|
||||
return;
|
||||
}
|
||||
ClockTime dur = null;
|
||||
synchronized (playbinLock) {
|
||||
playbin2.play(); // must play, then pause and get state to get duration.
|
||||
playbin2.pause();
|
||||
playbin2.getState();
|
||||
dur = playbin2.queryDuration();
|
||||
}
|
||||
duration = dur.toString();
|
||||
durationMillis = dur.toMillis();
|
||||
|
||||
// pick out the total hours, minutes, seconds
|
||||
long durationSeconds = (int) durationMillis / 1000;
|
||||
totalHours = (int) durationSeconds / 3600;
|
||||
durationSeconds -= totalHours * 3600;
|
||||
totalMinutes = (int) durationSeconds / 60;
|
||||
durationSeconds -= totalMinutes * 60;
|
||||
totalSeconds = (int) durationSeconds;
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
progressSlider.setMaximum((int) durationMillis);
|
||||
progressSlider.setMinimum(0);
|
||||
|
||||
synchronized (playbinLock) {
|
||||
playbin2.play();
|
||||
}
|
||||
pauseButton.setText("||");
|
||||
videoProgressWorker = new VideoProgressWorker();
|
||||
videoProgressWorker.execute();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class VideoProgressWorker extends SwingWorker<Object, Object> {
|
||||
|
||||
private String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d ";
|
||||
private long millisElapsed = 0;
|
||||
private final long INTER_FRAME_PERIOD_MS = 20;
|
||||
private final long END_TIME_MARGIN_MS = 50;
|
||||
|
||||
private boolean isPlayBinReady() {
|
||||
synchronized (playbinLock) {
|
||||
return playbin2 != null && !playbin2.getState().equals(State.NULL);
|
||||
}
|
||||
}
|
||||
|
||||
public void resetVideo() {
|
||||
synchronized (playbinLock) {
|
||||
if (playbin2 != null) {
|
||||
playbin2.stop();
|
||||
playbin2.setState(State.READY); // ready to be played again
|
||||
}
|
||||
}
|
||||
pauseButton.setText("►");
|
||||
progressSlider.setValue(0);
|
||||
|
||||
String durationStr = String.format(durationFormat, 0, 0, 0,
|
||||
totalHours, totalMinutes, totalSeconds);
|
||||
progressLabel.setText(durationStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true while millisElapsed is greater than END_TIME_MARGIN_MS
|
||||
* from durationMillis. This is used to indicate when the video has
|
||||
* ended because for some videos the time elapsed never becomes equal to
|
||||
* the reported duration of the video.
|
||||
*/
|
||||
private boolean hasNotEnded() {
|
||||
return (durationMillis - millisElapsed) > END_TIME_MARGIN_MS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
|
||||
// enable the slider
|
||||
progressSlider.setEnabled(true);
|
||||
|
||||
int elapsedHours = -1, elapsedMinutes = -1, elapsedSeconds = -1;
|
||||
ClockTime pos = null;
|
||||
while (hasNotEnded() && isPlayBinReady() && !isCancelled()) {
|
||||
|
||||
synchronized (playbinLock) {
|
||||
pos = playbin2.queryPosition();
|
||||
}
|
||||
millisElapsed = pos.toMillis();
|
||||
|
||||
// pick out the elapsed hours, minutes, seconds
|
||||
long secondsElapsed = millisElapsed / 1000;
|
||||
elapsedHours = (int) secondsElapsed / 3600;
|
||||
secondsElapsed -= elapsedHours * 3600;
|
||||
elapsedMinutes = (int) secondsElapsed / 60;
|
||||
secondsElapsed -= elapsedMinutes * 60;
|
||||
elapsedSeconds = (int) secondsElapsed;
|
||||
|
||||
String durationStr = String.format(durationFormat,
|
||||
elapsedHours, elapsedMinutes, elapsedSeconds,
|
||||
totalHours, totalMinutes, totalSeconds);
|
||||
|
||||
progressLabel.setText(durationStr);
|
||||
autoTracking = true;
|
||||
progressSlider.setValue((int) millisElapsed);
|
||||
autoTracking = false;
|
||||
|
||||
try {
|
||||
Thread.sleep(INTER_FRAME_PERIOD_MS);
|
||||
} catch (InterruptedException ex) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// disable the slider
|
||||
progressSlider.setEnabled(false);
|
||||
|
||||
resetVideo();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.4" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<Properties>
|
||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="0" red="0" type="rgb"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
|
||||
<Property name="axis" type="int" value="1"/>
|
||||
</Layout>
|
||||
</Form>
|
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.corecomponents;
|
||||
|
||||
import com.sun.javafx.application.PlatformImpl;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Level;
|
||||
import javafx.embed.swing.JFXPanel;
|
||||
import javafx.embed.swing.SwingFXUtils;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javax.imageio.ImageIO;
|
||||
import org.openide.modules.ModuleInstall;
|
||||
import org.openide.util.Lookup;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.ReadContentInputStream;
|
||||
|
||||
/**
|
||||
* Container for the image viewer part of media view, on a layered pane. To be
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Creates new form MediaViewImagePanel
|
||||
*/
|
||||
public MediaViewImagePanel() {
|
||||
initComponents();
|
||||
|
||||
|
||||
|
||||
org.sleuthkit.autopsy.core.Installer coreInstaller =
|
||||
ModuleInstall.findObject(org.sleuthkit.autopsy.core.Installer.class, false);
|
||||
if (coreInstaller != null) {
|
||||
fxInited = coreInstaller.isJavaFxInited();
|
||||
}
|
||||
|
||||
|
||||
if (fxInited) {
|
||||
setupFx();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInited() {
|
||||
return fxInited;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup FX components
|
||||
*/
|
||||
private void setupFx() {
|
||||
// load the image
|
||||
PlatformImpl.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
fxPanel = new JFXPanel();
|
||||
fxImageView = new ImageView();
|
||||
// resizes the image to have width of 100 while preserving the ratio and using
|
||||
// higher quality filtering method; this ImageView is also cached to
|
||||
// improve performance
|
||||
fxImageView.setPreserveRatio(true);
|
||||
fxImageView.setSmooth(true);
|
||||
fxImageView.setCache(true);
|
||||
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
add(fxPanel);
|
||||
|
||||
//TODO
|
||||
// setVisible(true);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Show image
|
||||
*
|
||||
* @param file image file to show
|
||||
* @param dims dimension of the parent window
|
||||
*/
|
||||
void showImageFx(final AbstractFile file, final Dimension dims) {
|
||||
if (!fxInited) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String fileName = file.getName();
|
||||
|
||||
//hide the panel during loading/transformations
|
||||
fxPanel.setVisible(false);
|
||||
|
||||
// load the image
|
||||
PlatformImpl.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!Case.isCaseOpen()) {
|
||||
//handle in-between condition when case is being closed
|
||||
//and an image was previously selected
|
||||
return;
|
||||
}
|
||||
|
||||
final InputStream inputStream = new ReadContentInputStream(file);
|
||||
|
||||
final Image fxImage;
|
||||
try {
|
||||
//original input stream
|
||||
BufferedImage bi = ImageIO.read(inputStream);
|
||||
//scale image using Scalr
|
||||
BufferedImage biScaled = ScalrWrapper.resizeHighQuality(bi, (int) dims.getWidth(), (int) dims.getHeight());
|
||||
//convert from awt imageto fx image
|
||||
fxImage = SwingFXUtils.toFXImage(biScaled, null);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Could not load image file into media view: " + fileName, ex);
|
||||
return;
|
||||
} catch (OutOfMemoryError ex) {
|
||||
logger.log(Level.WARNING, "Could not load image file into media view (too large): " + fileName, ex);
|
||||
MessageNotifyUtil.Notify.warn("Could not load image file (too large): " + file.getName(), ex.getMessage());
|
||||
return;
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Could not close input stream after loading image in media view: " + fileName, ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (fxImage == null || fxImage.isError()) {
|
||||
logger.log(Level.WARNING, "Could not load image file into media view: " + fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
//use border pane to center the image in the scene
|
||||
BorderPane borderpane = new BorderPane();
|
||||
borderpane.setCenter(fxImageView);
|
||||
|
||||
fxImageView.setImage(fxImage);
|
||||
fxImageView.setFitWidth(dims.getWidth());
|
||||
fxImageView.setFitHeight(dims.getHeight());
|
||||
|
||||
//Group fxRoot = new Group();
|
||||
|
||||
//Scene fxScene = new Scene(fxRoot, dims.getWidth(), dims.getHeight(), javafx.scene.paint.Color.BLACK);
|
||||
Scene fxScene = new Scene(borderpane, javafx.scene.paint.Color.BLACK);
|
||||
// borderpane.getChildren().add(fxImageView);
|
||||
|
||||
fxPanel.setScene(fxScene);
|
||||
|
||||
//show the panel after fully loaded
|
||||
fxPanel.setVisible(true);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
setBackground(new java.awt.Color(0, 0, 0));
|
||||
setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS));
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="videoPanel" alignment="1" max="32767" attributes="0"/>
|
||||
<Component id="controlPanel" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="videoPanel" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="controlPanel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="videoPanel">
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="188" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="controlPanel">
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="pauseButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="progressSlider" pref="357" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="progressLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="infoLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="pauseButton" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="progressSlider" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="progressLabel" alignment="1" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="infoLabel" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="pauseButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MediaViewVideoPanel.pauseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pauseButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JSlider" name="progressSlider">
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="progressLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MediaViewVideoPanel.progressLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="infoLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MediaViewVideoPanel.infoLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,646 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.corecomponents;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import org.gstreamer.ClockTime;
|
||||
import org.gstreamer.Gst;
|
||||
import org.gstreamer.GstException;
|
||||
import org.gstreamer.State;
|
||||
import org.gstreamer.elements.PlayBin2;
|
||||
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.util.Cancellable;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.openide.util.lookup.ServiceProviders;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
/**
|
||||
* Video viewer part of the Media View layered pane
|
||||
*/
|
||||
@ServiceProviders(value = {
|
||||
@ServiceProvider(service = FrameCapture.class)
|
||||
})
|
||||
public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapture {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName());
|
||||
private boolean gstInited;
|
||||
//frame capture
|
||||
private BufferedImage currentImage = null;
|
||||
private static final long MIN_FRAME_INTERVAL_MILLIS = 500;
|
||||
//playback
|
||||
private long durationMillis = 0;
|
||||
private VideoProgressWorker videoProgressWorker;
|
||||
private int totalHours, totalMinutes, totalSeconds;
|
||||
private volatile PlayBin2 gstPlaybin2;
|
||||
private VideoComponent gstVideoComponent;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Creates new form MediaViewVideoPanel
|
||||
*/
|
||||
public MediaViewVideoPanel() {
|
||||
initComponents();
|
||||
customizeComponents();
|
||||
}
|
||||
|
||||
public JButton getPauseButton() {
|
||||
return pauseButton;
|
||||
}
|
||||
|
||||
public JLabel getProgressLabel() {
|
||||
return progressLabel;
|
||||
}
|
||||
|
||||
public JSlider getProgressSlider() {
|
||||
return progressSlider;
|
||||
}
|
||||
|
||||
public JPanel getVideoPanel() {
|
||||
return videoPanel;
|
||||
}
|
||||
|
||||
public VideoComponent getVideoComponent() {
|
||||
return gstVideoComponent;
|
||||
}
|
||||
|
||||
public boolean isInited() {
|
||||
return gstInited;
|
||||
}
|
||||
|
||||
private void customizeComponents() {
|
||||
initGst();
|
||||
|
||||
progressSlider.setEnabled(false); // disable slider; enable after user plays vid
|
||||
progressSlider.setValue(0);
|
||||
|
||||
if (gstInited) {
|
||||
progressSlider.addChangeListener(new ChangeListener() {
|
||||
/**
|
||||
* Should always try to synchronize any call to
|
||||
* progressSlider.setValue() to avoid a different thread
|
||||
* changing playbin while stateChanged() is processing
|
||||
*/
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
int time = progressSlider.getValue();
|
||||
synchronized (playbinLock) {
|
||||
if (gstPlaybin2 != null && !autoTracking) {
|
||||
State orig = gstPlaybin2.getState();
|
||||
gstPlaybin2.pause();
|
||||
gstPlaybin2.seek(ClockTime.fromMillis(time));
|
||||
gstPlaybin2.setState(orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private boolean initGst() {
|
||||
try {
|
||||
logger.log(Level.INFO, "Initializing gstreamer for video/audio viewing");
|
||||
Gst.init();
|
||||
gstInited = true;
|
||||
} catch (GstException e) {
|
||||
gstInited = false;
|
||||
logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and frame extraction capabilities", e);
|
||||
MessageNotifyUtil.Notify.error("Error initializing gstreamer for audio/video viewing and frame extraction capabilities. "
|
||||
+ " Video and audio viewing will be disabled. ",
|
||||
e.getMessage());
|
||||
return false;
|
||||
} catch (UnsatisfiedLinkError | NoClassDefFoundError | Exception e) {
|
||||
gstInited = false;
|
||||
logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and extraction capabilities", e);
|
||||
MessageNotifyUtil.Notify.error("Error initializing gstreamer for audio/video viewing frame extraction capabilities. "
|
||||
+ " Video and audio viewing will be disabled. ",
|
||||
e.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all the necessary vars to play a video/audio file.
|
||||
*
|
||||
* @param file video file to play
|
||||
* @param dims dimension of the parent window
|
||||
*/
|
||||
void setupVideo(final AbstractFile file, final Dimension dims) {
|
||||
currentFile = file;
|
||||
final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC);
|
||||
if (deleted) {
|
||||
infoLabel.setText("Playback of deleted videos is not supported, use an external player. ");
|
||||
videoPanel.removeAll();
|
||||
pauseButton.setEnabled(false);
|
||||
progressSlider.setEnabled(false);
|
||||
return;
|
||||
} else {
|
||||
try {
|
||||
String path = file.getUniquePath();
|
||||
infoLabel.setText(path);
|
||||
infoLabel.setToolTipText(path);
|
||||
pauseButton.setEnabled(true);
|
||||
progressSlider.setEnabled(true);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Cannot get unique path of video file");
|
||||
}
|
||||
}
|
||||
|
||||
java.io.File ioFile = getJFile(file);
|
||||
|
||||
gstVideoComponent = new VideoComponent();
|
||||
synchronized (playbinLock) {
|
||||
if (gstPlaybin2 != null) {
|
||||
gstPlaybin2.dispose();
|
||||
}
|
||||
gstPlaybin2 = new PlayBin2("VideoPlayer");
|
||||
gstPlaybin2.setVideoSink(gstVideoComponent.getElement());
|
||||
|
||||
videoPanel.removeAll();
|
||||
|
||||
|
||||
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
||||
videoPanel.add(gstVideoComponent);
|
||||
|
||||
videoPanel.setVisible(true);
|
||||
|
||||
gstPlaybin2.setInputFile(ioFile);
|
||||
gstPlaybin2.setState(State.READY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void reset() {
|
||||
|
||||
// reset the progress label text on the event dispatch thread
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
progressLabel.setText("");
|
||||
// infoLabel.setText("");
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (!isInited()) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (playbinLock) {
|
||||
if (gstPlaybin2 != null) {
|
||||
if (gstPlaybin2.isPlaying()) {
|
||||
gstPlaybin2.stop();
|
||||
}
|
||||
gstPlaybin2.setState(State.NULL);
|
||||
if (gstPlaybin2.getState().equals(State.NULL)) {
|
||||
gstPlaybin2.dispose();
|
||||
}
|
||||
gstPlaybin2 = null;
|
||||
}
|
||||
gstVideoComponent = null;
|
||||
//videoComponent.setBackground(Color.BLACK);
|
||||
//videoComponent.repaint();
|
||||
|
||||
|
||||
//videoPanel.repaint();
|
||||
}
|
||||
|
||||
// get rid of any existing videoProgressWorker thread
|
||||
if (videoProgressWorker != null) {
|
||||
videoProgressWorker.cancel(true);
|
||||
videoProgressWorker = null;
|
||||
}
|
||||
|
||||
currentFile = null;
|
||||
}
|
||||
|
||||
private java.io.File getJFile(AbstractFile file) {
|
||||
// Get the temp folder path of the case
|
||||
String tempPath = Case.getCurrentCase().getTempDirectory();
|
||||
String name = file.getName();
|
||||
int extStart = name.lastIndexOf(".");
|
||||
String ext = "";
|
||||
if (extStart != -1) {
|
||||
ext = name.substring(extStart, name.length()).toLowerCase();
|
||||
}
|
||||
tempPath = tempPath + java.io.File.separator + file.getId() + ext;
|
||||
|
||||
java.io.File tempFile = new java.io.File(tempPath);
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VideoFrame> captureFrames(java.io.File file, int numFrames) {
|
||||
|
||||
List<VideoFrame> frames = new ArrayList<>();
|
||||
|
||||
if (!isInited()) {
|
||||
return frames;
|
||||
}
|
||||
|
||||
RGBDataSink.Listener listener1 = new RGBDataSink.Listener() {
|
||||
@Override
|
||||
public void rgbFrame(boolean bln, int w, int h, IntBuffer rgbPixels) {
|
||||
BufferedImage curImage = new BufferedImage(w, h,
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
curImage.setRGB(0, 0, w, h, rgbPixels.array(), 0, w);
|
||||
currentImage = curImage;
|
||||
}
|
||||
};
|
||||
|
||||
// set up a PlayBin2 object
|
||||
RGBDataSink videoSink = new RGBDataSink("rgb", listener1);
|
||||
PlayBin2 playbin = new PlayBin2("VideoFrameCapture");
|
||||
playbin.setInputFile(file);
|
||||
playbin.setVideoSink(videoSink);
|
||||
|
||||
// this is necessary to get a valid duration value
|
||||
playbin.play();
|
||||
playbin.pause();
|
||||
playbin.getState();
|
||||
|
||||
// get the duration of the video
|
||||
TimeUnit unit = TimeUnit.MILLISECONDS;
|
||||
long myDurationMillis = playbin.queryDuration(unit);
|
||||
if (myDurationMillis <= 0) {
|
||||
return frames;
|
||||
}
|
||||
|
||||
// create a list of timestamps at which to get frames
|
||||
int numFramesToGet = numFrames;
|
||||
long frameInterval = myDurationMillis / numFrames;
|
||||
if (frameInterval < MIN_FRAME_INTERVAL_MILLIS) {
|
||||
numFramesToGet = 1;
|
||||
}
|
||||
|
||||
// for each timeStamp, grap a frame
|
||||
for (int i = 0; i < numFramesToGet; ++i) {
|
||||
long timeStamp = i * frameInterval;
|
||||
|
||||
playbin.pause();
|
||||
playbin.getState();
|
||||
|
||||
currentImage = null;
|
||||
if (!playbin.seek(timeStamp, unit)) {
|
||||
logger.log(Level.INFO, "There was a problem seeking to " + timeStamp + " " + unit.name().toLowerCase());
|
||||
}
|
||||
playbin.play();
|
||||
|
||||
while (currentImage == null) {
|
||||
System.out.flush(); // not sure why this is needed
|
||||
}
|
||||
|
||||
playbin.stop();
|
||||
|
||||
frames.add(new VideoFrame(currentImage, timeStamp));
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
videoPanel = new javax.swing.JPanel();
|
||||
controlPanel = new javax.swing.JPanel();
|
||||
pauseButton = new javax.swing.JButton();
|
||||
progressSlider = new javax.swing.JSlider();
|
||||
progressLabel = new javax.swing.JLabel();
|
||||
infoLabel = new javax.swing.JLabel();
|
||||
|
||||
javax.swing.GroupLayout videoPanelLayout = new javax.swing.GroupLayout(videoPanel);
|
||||
videoPanel.setLayout(videoPanelLayout);
|
||||
videoPanelLayout.setHorizontalGroup(
|
||||
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 0, Short.MAX_VALUE)
|
||||
);
|
||||
videoPanelLayout.setVerticalGroup(
|
||||
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 188, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(pauseButton, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.pauseButton.text")); // NOI18N
|
||||
pauseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
pauseButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.progressLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(infoLabel, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.infoLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout controlPanelLayout = new javax.swing.GroupLayout(controlPanel);
|
||||
controlPanel.setLayout(controlPanelLayout);
|
||||
controlPanelLayout.setHorizontalGroup(
|
||||
controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(controlPanelLayout.createSequentialGroup()
|
||||
.addComponent(pauseButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, 357, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(progressLabel)
|
||||
.addContainerGap())
|
||||
.addGroup(controlPanelLayout.createSequentialGroup()
|
||||
.addComponent(infoLabel)
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
);
|
||||
controlPanelLayout.setVerticalGroup(
|
||||
controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(controlPanelLayout.createSequentialGroup()
|
||||
.addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(pauseButton)
|
||||
.addComponent(progressSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(progressLabel, javax.swing.GroupLayout.Alignment.TRAILING))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(infoLabel))
|
||||
);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(videoPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(controlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap())
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseButtonActionPerformed
|
||||
synchronized (playbinLock) {
|
||||
State state = gstPlaybin2.getState();
|
||||
if (state.equals(State.PLAYING)) {
|
||||
gstPlaybin2.pause();
|
||||
pauseButton.setText("►");
|
||||
gstPlaybin2.setState(State.PAUSED);
|
||||
} else if (state.equals(State.PAUSED)) {
|
||||
gstPlaybin2.play();
|
||||
pauseButton.setText("||");
|
||||
gstPlaybin2.setState(State.PLAYING);
|
||||
} else if (state.equals(State.READY)) {
|
||||
ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile));
|
||||
em.execute();
|
||||
em.getExtractedBytes();
|
||||
}
|
||||
}
|
||||
}//GEN-LAST:event_pauseButtonActionPerformed
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JPanel controlPanel;
|
||||
private javax.swing.JLabel infoLabel;
|
||||
private javax.swing.JButton pauseButton;
|
||||
private javax.swing.JLabel progressLabel;
|
||||
private javax.swing.JSlider progressSlider;
|
||||
private javax.swing.JPanel videoPanel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
private class VideoProgressWorker extends SwingWorker<Object, Object> {
|
||||
|
||||
private String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d ";
|
||||
private long millisElapsed = 0;
|
||||
private final long INTER_FRAME_PERIOD_MS = 20;
|
||||
private final long END_TIME_MARGIN_MS = 50;
|
||||
|
||||
private boolean isPlayBinReady() {
|
||||
synchronized (playbinLock) {
|
||||
return gstPlaybin2 != null && !gstPlaybin2.getState().equals(State.NULL);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetVideo() {
|
||||
synchronized (playbinLock) {
|
||||
if (gstPlaybin2 != null) {
|
||||
gstPlaybin2.stop();
|
||||
gstPlaybin2.setState(State.READY); // ready to be played again
|
||||
gstPlaybin2.getState(); //NEW
|
||||
}
|
||||
}
|
||||
pauseButton.setText("►");
|
||||
progressSlider.setValue(0);
|
||||
|
||||
String durationStr = String.format(durationFormat, 0, 0, 0,
|
||||
totalHours, totalMinutes, totalSeconds);
|
||||
progressLabel.setText(durationStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true while millisElapsed is greater than END_TIME_MARGIN_MS
|
||||
* from durationMillis. This is used to indicate when the video has
|
||||
* ended because for some videos the time elapsed never becomes equal to
|
||||
* the reported duration of the video.
|
||||
*/
|
||||
private boolean hasNotEnded() {
|
||||
return (durationMillis - millisElapsed) > END_TIME_MARGIN_MS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
|
||||
// enable the slider
|
||||
progressSlider.setEnabled(true);
|
||||
|
||||
int elapsedHours = -1, elapsedMinutes = -1, elapsedSeconds = -1;
|
||||
ClockTime pos = null;
|
||||
while (hasNotEnded() && isPlayBinReady() && !isCancelled()) {
|
||||
|
||||
synchronized (playbinLock) {
|
||||
pos = gstPlaybin2.queryPosition();
|
||||
}
|
||||
millisElapsed = pos.toMillis();
|
||||
|
||||
// pick out the elapsed hours, minutes, seconds
|
||||
long secondsElapsed = millisElapsed / 1000;
|
||||
elapsedHours = (int) secondsElapsed / 3600;
|
||||
secondsElapsed -= elapsedHours * 3600;
|
||||
elapsedMinutes = (int) secondsElapsed / 60;
|
||||
secondsElapsed -= elapsedMinutes * 60;
|
||||
elapsedSeconds = (int) secondsElapsed;
|
||||
|
||||
String durationStr = String.format(durationFormat,
|
||||
elapsedHours, elapsedMinutes, elapsedSeconds,
|
||||
totalHours, totalMinutes, totalSeconds);
|
||||
|
||||
progressLabel.setText(durationStr);
|
||||
autoTracking = true;
|
||||
progressSlider.setValue((int) millisElapsed);
|
||||
autoTracking = false;
|
||||
|
||||
try {
|
||||
Thread.sleep(INTER_FRAME_PERIOD_MS);
|
||||
} catch (InterruptedException ex) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// disable the slider
|
||||
progressSlider.setEnabled(false);
|
||||
|
||||
resetVideo();
|
||||
|
||||
return null;
|
||||
}
|
||||
} //end class progress worker
|
||||
|
||||
/* Thread that extracts and plays a file */
|
||||
private class ExtractMedia extends SwingWorker<Object, Void> {
|
||||
|
||||
private ProgressHandle progress;
|
||||
boolean success = false;
|
||||
private AbstractFile sFile;
|
||||
private java.io.File jFile;
|
||||
private String duration;
|
||||
private String position;
|
||||
private long extractedBytes;
|
||||
|
||||
ExtractMedia(org.sleuthkit.datamodel.AbstractFile sFile, java.io.File jFile) {
|
||||
this.sFile = sFile;
|
||||
this.jFile = jFile;
|
||||
}
|
||||
|
||||
public long getExtractedBytes() {
|
||||
return extractedBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
success = false;
|
||||
progress = ProgressHandleFactory.createHandle("Buffering " + sFile.getName(), new Cancellable() {
|
||||
@Override
|
||||
public boolean cancel() {
|
||||
return ExtractMedia.this.cancel(true);
|
||||
}
|
||||
});
|
||||
progressLabel.setText("Buffering... ");
|
||||
progress.start();
|
||||
progress.switchToDeterminate(100);
|
||||
try {
|
||||
extractedBytes = ContentUtils.writeToFile(sFile, jFile, progress, this, true);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Error buffering file", ex);
|
||||
}
|
||||
success = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
/* clean up or start the worker threads */
|
||||
@Override
|
||||
protected void done() {
|
||||
try {
|
||||
super.get(); //block and get all exceptions thrown while doInBackground()
|
||||
} catch (CancellationException ex) {
|
||||
logger.log(Level.INFO, "Media buffering was canceled.");
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.INFO, "Media buffering was interrupted.");
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Fatal error during media buffering.", ex);
|
||||
} finally {
|
||||
progress.finish();
|
||||
if (!this.isCancelled()) {
|
||||
playMedia();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void playMedia() {
|
||||
if (jFile == null || !jFile.exists()) {
|
||||
progressLabel.setText("Error buffering file");
|
||||
return;
|
||||
}
|
||||
ClockTime dur = null;
|
||||
synchronized (playbinLock) {
|
||||
gstPlaybin2.play(); // must play, then pause and get state to get duration.
|
||||
gstPlaybin2.pause();
|
||||
State state = gstPlaybin2.getState();
|
||||
dur = gstPlaybin2.queryDuration();
|
||||
}
|
||||
duration = dur.toString();
|
||||
durationMillis = dur.toMillis();
|
||||
|
||||
// pick out the total hours, minutes, seconds
|
||||
long durationSeconds = (int) durationMillis / 1000;
|
||||
totalHours = (int) durationSeconds / 3600;
|
||||
durationSeconds -= totalHours * 3600;
|
||||
totalMinutes = (int) durationSeconds / 60;
|
||||
durationSeconds -= totalMinutes * 60;
|
||||
totalSeconds = (int) durationSeconds;
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
progressSlider.setMaximum((int) durationMillis);
|
||||
progressSlider.setMinimum(0);
|
||||
|
||||
synchronized (playbinLock) {
|
||||
gstPlaybin2.play();
|
||||
}
|
||||
pauseButton.setText("||");
|
||||
videoProgressWorker = new VideoProgressWorker();
|
||||
videoProgressWorker.execute();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -27,7 +27,6 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dfickling
|
||||
*/
|
||||
public class ExtractedContentChildren extends ChildFactory<BlackboardArtifact.ARTIFACT_TYPE> {
|
||||
|
||||
@ -51,6 +50,9 @@ public class ExtractedContentChildren extends ChildFactory<BlackboardArtifact.AR
|
||||
list.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(BlackboardArtifact.ARTIFACT_TYPE key){
|
||||
|
@ -63,6 +63,7 @@ public class TagFileAction extends AbstractAction implements Presenter.Popup {
|
||||
}
|
||||
|
||||
private void refreshDirectoryTree() {
|
||||
//TODO instead should send event to node children, which will call its refresh() / refreshKeys()
|
||||
DirectoryTreeTopComponent viewer = DirectoryTreeTopComponent.findInstance();
|
||||
viewer.refreshTree(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE);
|
||||
viewer.refreshTree(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT);
|
||||
|
@ -78,6 +78,8 @@ public final class ExifParserFileIngestModule implements IngestModuleAbstractFil
|
||||
private static final Logger logger = Logger.getLogger(ExifParserFileIngestModule.class.getName());
|
||||
private static ExifParserFileIngestModule defaultInstance = null;
|
||||
private static int messageId = 0;
|
||||
|
||||
private int filesProcessed = 0;
|
||||
|
||||
//file ingest modules require a private constructor
|
||||
//to ensure singleton instances
|
||||
@ -164,7 +166,10 @@ public final class ExifParserFileIngestModule implements IngestModuleAbstractFil
|
||||
if(!attributes.isEmpty()) {
|
||||
BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF);
|
||||
bba.addAttributes(attributes);
|
||||
services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF));
|
||||
++filesProcessed;
|
||||
if (filesProcessed %100 == 0) {
|
||||
services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF));
|
||||
}
|
||||
}
|
||||
|
||||
return IngestModuleAbstractFile.ProcessResult.OK;
|
||||
@ -243,6 +248,10 @@ public final class ExifParserFileIngestModule implements IngestModuleAbstractFil
|
||||
public void complete() {
|
||||
logger.log(Level.INFO, "completed exif parsing " + this.toString());
|
||||
|
||||
if (filesProcessed > 0) {
|
||||
//send the final new data event
|
||||
services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF));
|
||||
}
|
||||
//module specific cleanup due to completion here
|
||||
}
|
||||
|
||||
@ -277,6 +286,8 @@ public final class ExifParserFileIngestModule implements IngestModuleAbstractFil
|
||||
public void init(IngestModuleInit initContext) {
|
||||
services = IngestServices.getDefault();
|
||||
logger.log(Level.INFO, "init() " + this.toString());
|
||||
|
||||
filesProcessed = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
4
NEWS.txt
4
NEWS.txt
@ -13,12 +13,10 @@ Improvements:
|
||||
|
||||
Bugfixes:
|
||||
- fixed memory leaks in "Add Image"
|
||||
- fix bug with duplicate instances of content and result viewers hidden and possibly degrading performance
|
||||
- The "media view" tab is inactive for deleted files (#165)
|
||||
- show error message in hex and string viewer if specific offset of a file could not be read.
|
||||
- file search actions not always enabled when new case is open.
|
||||
- fixed directory tree history being reset when tree is refreshed.
|
||||
- The "media view" tab is inactive for deleted files (#165)
|
||||
|
||||
|
||||
---------------- VERSION 3.0.4 --------------
|
||||
|
||||
|
13
Test/script/Setup.txt
Normal file
13
Test/script/Setup.txt
Normal file
@ -0,0 +1,13 @@
|
||||
This file explains how to setup regression and continuous testing for Autopsy.
|
||||
|
||||
1)Create a folder called 'gold', this is where the test standards will go.
|
||||
2)Create a folder called 'input', this is where the image, hash, and keyword search files.
|
||||
3)Put an idx file called NSRL, that contains known bad hashes, in the input folder.
|
||||
4)Put an idx file called notablehashes, that contains notable hashes, in the input folder.
|
||||
5)Put an xml file called notablekeywords, that contains keywords, in the input folder.
|
||||
6)The xml file should be of the format of the notablekeywords-example.xml file.
|
||||
7)Create a folder called 'output', this is where the output from tests go.
|
||||
8)The config.xml file is an example of a configuration file and contains all of the available elements.
|
||||
9)Use the -g command to create a gold standard without testing.
|
||||
10)Use the -r command to rebuild gold standards with testing.
|
||||
11)Use the -c command to run testing continuously until interrupted, this will also update all of the sleuthkit and autopsy code from github and will rebuild the code so that it is running the latest version after each test.
|
@ -35,6 +35,7 @@ It is up to the user to distinguish between the paths when adding to this file.
|
||||
<image value="X:\path\to\a\different\image\file.E01" />
|
||||
<image value="This provokes an error." />
|
||||
<image value="X:\Invalid\Path\Does\Not\Exist.img" />
|
||||
<build value="X:\Path\To\Build\File.xml">
|
||||
<email value="address@provider.com"/>
|
||||
<mail_server value="mail.localhost.com" />
|
||||
</Properties>
|
@ -24,6 +24,8 @@ from email.MIMEBase import MIMEBase
|
||||
from email import Encoders
|
||||
import urllib2
|
||||
import re
|
||||
import zipfile
|
||||
import zlib
|
||||
|
||||
#
|
||||
# Please read me...
|
||||
@ -66,6 +68,7 @@ class Args:
|
||||
self.exception = False
|
||||
self.exception_string = ""
|
||||
self.contin = False
|
||||
self.gold_creation = False
|
||||
|
||||
def parse(self):
|
||||
global nxtproc
|
||||
@ -103,7 +106,7 @@ class Args:
|
||||
printout("Ignoring unallocated space.\n")
|
||||
self.unallocated = True
|
||||
elif(arg == "-i" or arg == "--ignore"):
|
||||
printout("Ignoring the ./input directory.\n")
|
||||
printout("Ignoring the ../input directory.\n")
|
||||
self.ignore = True
|
||||
elif(arg == "-k" or arg == "--keep"):
|
||||
printout("Keeping the Solr index.\n")
|
||||
@ -128,6 +131,9 @@ class Args:
|
||||
elif arg == "-c" or arg == "--continuous":
|
||||
printout("Running until interrupted")
|
||||
self.contin = True
|
||||
elif arg == "-g" or arg == "--gold":
|
||||
printout("Creating gold standards")
|
||||
self.gold_creation = True
|
||||
else:
|
||||
printout(usage())
|
||||
return False
|
||||
@ -142,9 +148,9 @@ class Args:
|
||||
class TestAutopsy:
|
||||
def __init__(self):
|
||||
# Paths:
|
||||
self.input_dir = make_local_path("input")
|
||||
self.input_dir = make_local_path("..","input")
|
||||
self.output_dir = ""
|
||||
self.gold = "gold"
|
||||
self.gold = make_local_path("..", "output", "gold")
|
||||
# Logs:
|
||||
self.antlog_dir = ""
|
||||
self.common_log = ""
|
||||
@ -165,6 +171,7 @@ class TestAutopsy:
|
||||
self.known_bad_path = ""
|
||||
self.keyword_path = ""
|
||||
self.nsrl_path = ""
|
||||
self.build_path = ""
|
||||
# Case info
|
||||
self.start_date = ""
|
||||
self.end_date = ""
|
||||
@ -300,7 +307,7 @@ class Database:
|
||||
|
||||
def generate_autopsy_artifacts(self):
|
||||
if not self.autopsy_artifacts:
|
||||
autopsy_db_file = os.path.join("./", case.output_dir, case.image_name,
|
||||
autopsy_db_file = make_path(case.output_dir, case.image_name,
|
||||
"AutopsyTestCase", "autopsy.db")
|
||||
autopsy_con = sqlite3.connect(autopsy_db_file)
|
||||
autopsy_cur = autopsy_con.cursor()
|
||||
@ -309,10 +316,16 @@ class Database:
|
||||
for type_id in range(1, length):
|
||||
autopsy_cur.execute("SELECT COUNT(*) FROM blackboard_artifacts WHERE artifact_type_id=%d" % type_id)
|
||||
self.autopsy_artifacts.append(autopsy_cur.fetchone()[0])
|
||||
autopsy_cur.execute("SELECT * FROM blackboard_artifacts")
|
||||
self.autopsy_artifacts_list = []
|
||||
for row in autopsy_cur.fetchall():
|
||||
for item in row:
|
||||
self.autopsy_artifacts_list.append(item)
|
||||
|
||||
|
||||
def generate_autopsy_attributes(self):
|
||||
if self.autopsy_attributes == 0:
|
||||
autopsy_db_file = os.path.join("./", case.output_dir, case.image_name,
|
||||
autopsy_db_file = make_path(case.output_dir, case.image_name,
|
||||
"AutopsyTestCase", "autopsy.db")
|
||||
autopsy_con = sqlite3.connect(autopsy_db_file)
|
||||
autopsy_cur = autopsy_con.cursor()
|
||||
@ -322,7 +335,7 @@ class Database:
|
||||
|
||||
def generate_autopsy_objects(self):
|
||||
if self.autopsy_objects == 0:
|
||||
autopsy_db_file = os.path.join("./", case.output_dir, case.image_name,
|
||||
autopsy_db_file = make_path(case.output_dir, case.image_name,
|
||||
"AutopsyTestCase", "autopsy.db")
|
||||
autopsy_con = sqlite3.connect(autopsy_db_file)
|
||||
autopsy_cur = autopsy_con.cursor()
|
||||
@ -332,7 +345,7 @@ class Database:
|
||||
|
||||
def generate_gold_artifacts(self):
|
||||
if not self.gold_artifacts:
|
||||
gold_db_file = os.path.join("./", case.gold, case.image_name, "autopsy.db")
|
||||
gold_db_file = make_path(case.gold, case.image_name, "autopsy.db")
|
||||
gold_con = sqlite3.connect(gold_db_file)
|
||||
gold_cur = gold_con.cursor()
|
||||
gold_cur.execute("SELECT COUNT(*) FROM blackboard_artifact_types")
|
||||
@ -340,10 +353,15 @@ class Database:
|
||||
for type_id in range(1, length):
|
||||
gold_cur.execute("SELECT COUNT(*) FROM blackboard_artifacts WHERE artifact_type_id=%d" % type_id)
|
||||
self.gold_artifacts.append(gold_cur.fetchone()[0])
|
||||
gold_cur.execute("SELECT * FROM blackboard_artifacts")
|
||||
self.gold_artifacts_list = []
|
||||
for row in gold_cur.fetchall():
|
||||
for item in row:
|
||||
self.gold_artifacts_list.append(item)
|
||||
|
||||
def generate_gold_attributes(self):
|
||||
if self.gold_attributes == 0:
|
||||
gold_db_file = os.path.join("./", case.gold, case.image_name, "autopsy.db")
|
||||
gold_db_file = make_path(case.gold, case.image_name, "autopsy.db")
|
||||
gold_con = sqlite3.connect(gold_db_file)
|
||||
gold_cur = gold_con.cursor()
|
||||
gold_cur.execute("SELECT COUNT(*) FROM blackboard_attributes")
|
||||
@ -351,7 +369,7 @@ class Database:
|
||||
|
||||
def generate_gold_objects(self):
|
||||
if self.gold_objects == 0:
|
||||
gold_db_file = os.path.join("./", case.gold, case.image_name, "autopsy.db")
|
||||
gold_db_file = make_path(case.gold, case.image_name, "autopsy.db")
|
||||
gold_con = sqlite3.connect(gold_db_file)
|
||||
gold_cur = gold_con.cursor()
|
||||
gold_cur.execute("SELECT COUNT(*) FROM tsk_objects")
|
||||
@ -462,6 +480,7 @@ def compile():
|
||||
# Runs the test on the single given file.
|
||||
# The path must be guarenteed to be a correct path.
|
||||
def run_test(image_file, count):
|
||||
global parsed
|
||||
if image_type(image_file) == IMGTYPE.UNKNOWN:
|
||||
printerror("Error: Image type is unrecognized:")
|
||||
printerror(image_file + "\n")
|
||||
@ -474,6 +493,18 @@ def run_test(image_file, count):
|
||||
case.common_log_path = make_local_path(case.output_dir, case.image_name, case.image_name+case.common_log)
|
||||
case.warning_log = make_local_path(case.output_dir, case.image_name, "AutopsyLogs.txt")
|
||||
case.antlog_dir = make_local_path(case.output_dir, case.image_name, "antlog.txt")
|
||||
if(args.list):
|
||||
element = parsed.getElementsByTagName("build")
|
||||
if(len(element)<=0):
|
||||
toval = make_path("..", "build.xml")
|
||||
else:
|
||||
element = element[0]
|
||||
toval = element.getAttribute("value").encode().decode("utf_8")
|
||||
if(toval==None):
|
||||
toval = make_path("..", "build.xml")
|
||||
else:
|
||||
toval = make_path("..", "build.xml")
|
||||
case.build_path = toval
|
||||
case.known_bad_path = make_path(case.input_dir, "notablehashes.txt-md5.idx")
|
||||
case.keyword_path = make_path(case.input_dir, "notablekeywords.xml")
|
||||
case.nsrl_path = make_path(case.input_dir, "nsrl.txt-md5.idx")
|
||||
@ -512,19 +543,31 @@ def run_test(image_file, count):
|
||||
print_report(exceptions, "EXCEPTION", okay)
|
||||
|
||||
# Now test in comparison to the gold standards
|
||||
compare_to_gold_db()
|
||||
compare_to_gold_html()
|
||||
|
||||
if not args.gold_creation:
|
||||
try:
|
||||
gold_path = case.gold
|
||||
img_gold = make_path(case.gold, case.image_name)
|
||||
img_archive = make_path(case.gold, case.image_name+"-archive.zip")
|
||||
extrctr = zipfile.ZipFile(img_archive, 'r', compression=zipfile.ZIP_DEFLATED)
|
||||
extrctr.extractall(gold_path)
|
||||
extrctr.close
|
||||
time.sleep(1)
|
||||
compare_to_gold_db()
|
||||
compare_to_gold_html()
|
||||
compare_errors()
|
||||
del_dir(img_gold)
|
||||
except:
|
||||
print("Tests failed due to an error, try rebuilding or creating gold standards.\n")
|
||||
# Make the CSV log and the html log viewer
|
||||
generate_csv(case.csv)
|
||||
if case.global_csv:
|
||||
generate_csv(case.global_csv)
|
||||
generate_html()
|
||||
# If running in rebuild mode (-r)
|
||||
if args.rebuild:
|
||||
if args.rebuild or args.gold_creation:
|
||||
rebuild()
|
||||
# Reset the case and return the tests sucessfully finished
|
||||
clear_dir(make_local_path(case.output_dir, case.image_name, "AutopsyTestCase", "ModuleOutput", "keywordsearch"))
|
||||
clear_dir(make_path(case.output_dir, case.image_name, "AutopsyTestCase", "ModuleOutput", "keywordsearch"))
|
||||
case.reset()
|
||||
return True
|
||||
|
||||
@ -536,12 +579,11 @@ def run_ant():
|
||||
if dir_exists(test_case_path):
|
||||
shutil.rmtree(test_case_path)
|
||||
os.makedirs(test_case_path)
|
||||
if not dir_exists(make_local_path("gold")):
|
||||
os.makedirs(make_local_path("gold"))
|
||||
case.ant = ["ant"]
|
||||
case.ant.append("-v")
|
||||
case.ant.append("-f")
|
||||
case.ant.append(os.path.join("..","build.xml"))
|
||||
# case.ant.append(case.build_path)
|
||||
case.ant.append(os.path.join("..","..","Testing","build.xml"))
|
||||
case.ant.append("regression-test")
|
||||
case.ant.append("-l")
|
||||
case.ant.append(case.antlog_dir)
|
||||
@ -549,7 +591,7 @@ def run_ant():
|
||||
case.ant.append("-Dknown_bad_path=" + case.known_bad_path)
|
||||
case.ant.append("-Dkeyword_path=" + case.keyword_path)
|
||||
case.ant.append("-Dnsrl_path=" + case.nsrl_path)
|
||||
case.ant.append("-Dgold_path=" + make_local_path(case.gold))
|
||||
case.ant.append("-Dgold_path=" + make_path(case.gold))
|
||||
case.ant.append("-Dout_path=" + make_local_path(case.output_dir, case.image_name))
|
||||
case.ant.append("-Dignore_unalloc=" + "%s" % args.unallocated)
|
||||
case.ant.append("-Dcontin_mode=" + str(args.contin))
|
||||
@ -563,7 +605,7 @@ def run_ant():
|
||||
if SYS is OS.CYGWIN:
|
||||
subprocess.call(case.ant, stdout=antout)
|
||||
elif SYS is OS.WIN:
|
||||
theproc = subprocess.Popen(case.ant, shell = True)
|
||||
theproc = subprocess.Popen(case.ant, shell = True, stdout=subprocess.PIPE)
|
||||
theproc.communicate()
|
||||
antout.close()
|
||||
|
||||
@ -597,12 +639,14 @@ def rebuild():
|
||||
# Errors to print
|
||||
errors = []
|
||||
# Delete the current gold standards
|
||||
gold_dir = make_local_path(case.gold, case.image_name)
|
||||
gold_dir = make_path(case.gold, case.image_name)
|
||||
clear_dir(gold_dir)
|
||||
dbinpth = make_local_path(case.output_dir, case.image_name, "AutopsyTestCase", "autopsy.db")
|
||||
dboutpth = make_local_path(case.gold, case.image_name, "autopsy.db")
|
||||
dbinpth = make_path(case.output_dir, case.image_name, "AutopsyTestCase", "autopsy.db")
|
||||
dboutpth = make_path(case.gold, case.image_name, "autopsy.db")
|
||||
if not os.path.exists(gold_dir):
|
||||
os.makedirs(gold_dir)
|
||||
copy_file(dbinpth, dboutpth)
|
||||
error_pth = make_local_path(case.gold, case.image_name, case.image_name+"SortedErrors.txt")
|
||||
error_pth = make_path(case.gold, case.image_name, case.image_name+"SortedErrors.txt")
|
||||
copy_file(case.sorted_log, error_pth)
|
||||
# Rebuild the HTML report
|
||||
htmlfolder = ""
|
||||
@ -611,29 +655,43 @@ def rebuild():
|
||||
htmlfolder = fs
|
||||
autopsy_html_path = make_local_path(case.output_dir, case.image_name, "AutopsyTestCase", "Reports", htmlfolder)
|
||||
|
||||
html_path = make_local_path(case.output_dir, case.image_name,
|
||||
html_path = make_path(case.output_dir, case.image_name,
|
||||
"AutopsyTestCase", "Reports")
|
||||
try:
|
||||
os.makedirs(os.path.join(os.getcwd(), case.gold, case.image_name, htmlfolder))
|
||||
os.makedirs(os.path.join(case.gold, case.image_name, htmlfolder))
|
||||
for file in os.listdir(autopsy_html_path):
|
||||
html_to = make_local_path(case.gold, case.image_name, file.replace("HTML Report", "Report"))
|
||||
html_to = make_path(case.gold, case.image_name, file.replace("HTML Report", "Report"))
|
||||
copy_dir(get_file_in_dir(autopsy_html_path, file), html_to)
|
||||
except FileNotFoundException as e:
|
||||
errors.append(e.error)
|
||||
except Exception as e:
|
||||
errors.append("Error: Unknown fatal error when rebuilding the gold html report.")
|
||||
errors.append(str(e) + "\n")
|
||||
oldcwd = os.getcwd()
|
||||
os.chdir(case.gold)
|
||||
img_archive = make_path(case.image_name+"-archive.zip")
|
||||
img_gold = os.path.join(case.image_name)
|
||||
comprssr = zipfile.ZipFile(img_archive, 'w',compression=zipfile.ZIP_DEFLATED)
|
||||
zipdir(img_gold, comprssr)
|
||||
comprssr.close()
|
||||
del_dir(gold_dir)
|
||||
os.chdir(oldcwd)
|
||||
okay = "Sucessfully rebuilt all gold standards."
|
||||
print_report(errors, "REBUILDING", okay)
|
||||
|
||||
def zipdir(path, zip):
|
||||
for root, dirs, files in os.walk(path):
|
||||
for file in files:
|
||||
zip.write(os.path.join(root, file))
|
||||
|
||||
# Using the global case's variables, compare the database file made by the
|
||||
# regression test to the gold standard database file
|
||||
# Initializes the global database, which stores the information retrieved
|
||||
# from queries while comparing
|
||||
def compare_to_gold_db():
|
||||
# SQLITE needs unix style pathing
|
||||
gold_db_file = os.path.join("./", case.gold, case.image_name, "autopsy.db")
|
||||
autopsy_db_file = os.path.join("./", case.output_dir, case.image_name,
|
||||
gold_db_file = make_path(case.gold, case.image_name, "autopsy.db")
|
||||
autopsy_db_file = make_path(case.output_dir, case.image_name,
|
||||
"AutopsyTestCase", "autopsy.db")
|
||||
# Try to query the databases. Ignore any exceptions, the function will
|
||||
# return an error later on if these do fail
|
||||
@ -687,13 +745,12 @@ def compare_to_gold_db():
|
||||
# Using the global case's variables, compare the html report file made by
|
||||
# the regression test against the gold standard html report
|
||||
def compare_to_gold_html():
|
||||
gold_html_file = make_local_path(case.gold, case.image_name, "Report", "index.html")
|
||||
gold_html_file = make_path(case.gold, case.image_name, "Report", "index.html")
|
||||
htmlfolder = ""
|
||||
for fs in os.listdir(os.path.join(os.getcwd(),case.output_dir, case.image_name, "AutopsyTestCase", "Reports")):
|
||||
if os.path.isdir(os.path.join(os.getcwd(), case.output_dir, case.image_name, "AutopsyTestCase", "Reports", fs)):
|
||||
for fs in os.listdir(make_path(case.output_dir, case.image_name, "AutopsyTestCase", "Reports")):
|
||||
if os.path.isdir(make_path(case.output_dir, case.image_name, "AutopsyTestCase", "Reports", fs)):
|
||||
htmlfolder = fs
|
||||
autopsy_html_path = make_local_path(case.output_dir, case.image_name, "AutopsyTestCase", "Reports", htmlfolder, "HTML Report") #, "AutopsyTestCase", "Reports", htmlfolder)
|
||||
print(autopsy_html_path)
|
||||
autopsy_html_path = make_path(case.output_dir, case.image_name, "AutopsyTestCase", "Reports", htmlfolder, "HTML Report") #, "AutopsyTestCase", "Reports", htmlfolder)
|
||||
|
||||
|
||||
try:
|
||||
@ -708,14 +765,14 @@ def compare_to_gold_html():
|
||||
return
|
||||
#Find all gold .html files belonging to this case
|
||||
ListGoldHTML = []
|
||||
for fs in os.listdir(os.path.join(case.output_dir, case.image_name, "AutopsyTestCase", "Reports", htmlfolder)):
|
||||
for fs in os.listdir(make_path(case.output_dir, case.image_name, "AutopsyTestCase", "Reports", htmlfolder)):
|
||||
if(fs.endswith(".html")):
|
||||
ListGoldHTML.append(os.path.join(case.output_dir, case.image_name, "AutopsyTestCase", "Reports", htmlfolder, fs))
|
||||
#Find all new .html files belonging to this case
|
||||
ListNewHTML = []
|
||||
for fs in os.listdir(os.path.join(case.gold, case.image_name)):
|
||||
for fs in os.listdir(make_path(case.gold, case.image_name)):
|
||||
if (fs.endswith(".html")):
|
||||
ListNewHTML.append(os.path.join(case.gold, case.image_name, fs))
|
||||
ListNewHTML.append(make_path(case.gold, case.image_name, fs))
|
||||
#ensure both reports have the same number of files and are in the same order
|
||||
if(len(ListGoldHTML) != len(ListNewHTML)):
|
||||
printerror("The reports did not have the same number of files. One of the reports may have been corrupted")
|
||||
@ -813,7 +870,6 @@ def generate_common_log():
|
||||
try:
|
||||
logs_path = make_local_path(case.output_dir, case.image_name, "logs")
|
||||
common_log = codecs.open(case.common_log_path, "w", "utf_8")
|
||||
print(case.common_log_path)
|
||||
warning_log = codecs.open(case.warning_log, "w", "utf_8")
|
||||
common_log.write("--------------------------------------------------\n")
|
||||
common_log.write(case.image_name + "\n")
|
||||
@ -840,14 +896,13 @@ def generate_common_log():
|
||||
case.sorted_log = make_local_path(case.output_dir, case.image_name, case.image_name + "SortedErrors.txt")
|
||||
srtcmdlst = ["sort", case.common_log_path, "-o", case.sorted_log]
|
||||
subprocess.call(srtcmdlst)
|
||||
compare_errors()
|
||||
except Exception as e:
|
||||
printerror("Error: Unable to generate the common log.")
|
||||
printerror(str(e) + "\n")
|
||||
logging.critical(traceback.format_exc())
|
||||
|
||||
def compare_errors():
|
||||
gold_dir = make_local_path(case.gold, case.image_name, case.image_name + "SortedErrors.txt")
|
||||
gold_dir = make_path(case.gold, case.image_name, case.image_name + "SortedErrors.txt")
|
||||
common_log = codecs.open(case.sorted_log, "r", "utf_8")
|
||||
gold_log = codecs.open(gold_dir, "r", "utf_8")
|
||||
gold_dat = gold_log.read()
|
||||
@ -890,9 +945,10 @@ def fill_case_data():
|
||||
start = datetime.datetime.strptime(case.start_date, "%b %d, %Y %I:%M:%S %p")
|
||||
end = datetime.datetime.strptime(case.end_date, "%a %b %d %H:%M:%S %Y")
|
||||
case.total_test_time = str(end - start)
|
||||
|
||||
|
||||
try:
|
||||
# Set Autopsy version, heap space, ingest time, and service times
|
||||
|
||||
version_line = search_logs("INFO: Application name: Autopsy, version:")[0]
|
||||
case.autopsy_version = get_word_at(version_line, 5).rstrip(",")
|
||||
|
||||
@ -1169,7 +1225,6 @@ def generate_html():
|
||||
write_html_head()
|
||||
try:
|
||||
global html
|
||||
print(case.html_log)
|
||||
html = open(case.html_log, "a")
|
||||
# The image title
|
||||
title = "<h1><a name='" + case.image_name + "'>" + case.image_name + " \
|
||||
@ -1490,7 +1545,7 @@ def wgetcwd():
|
||||
# Copy the log files from Autopsy's default directory
|
||||
def copy_logs():
|
||||
try:
|
||||
log_dir = os.path.join("..","build","test","qa-functional","work","userdir0","var","log")
|
||||
log_dir = os.path.join("..", "..", "Testing","build","test","qa-functional","work","userdir0","var","log")
|
||||
shutil.copytree(log_dir, make_local_path(case.output_dir, case.image_name, "logs"))
|
||||
except Exception as e:
|
||||
printerror("Error: Failed to copy the logs.")
|
||||
@ -1521,6 +1576,7 @@ def del_dir(dir):
|
||||
def copy_file(ffrom, to):
|
||||
try :
|
||||
if not file_exists(ffrom):
|
||||
print("hi")
|
||||
raise FileNotFoundException(ffrom)
|
||||
shutil.copy(ffrom, to)
|
||||
except:
|
||||
@ -1604,19 +1660,19 @@ def usage():
|
||||
Usage: ./regression.py [-f FILE] [OPTIONS]
|
||||
|
||||
Run RegressionTest.java, and compare the result with a gold standard.
|
||||
By default, the script tests every image in ./input
|
||||
By default, the script tests every image in ../input
|
||||
When the -f flag is set, this script only tests a single given image.
|
||||
When the -l flag is set, the script looks for a configuration file,
|
||||
which may outsource to a new input directory and to individual images.
|
||||
|
||||
Expected files:
|
||||
An NSRL database at: ./input/nsrl.txt-md5.idx
|
||||
A notable hash database at: ./input/notablehashes.txt-md5.idx
|
||||
A notable keyword file at: ./input/notablekeywords.xml
|
||||
An NSRL database at: ../input/nsrl.txt-md5.idx
|
||||
A notable hash database at: ../input/notablehashes.txt-md5.idx
|
||||
A notable keyword file at: ../input/notablekeywords.xml
|
||||
|
||||
Options:
|
||||
-r Rebuild the gold standards for the image(s) tested.
|
||||
-i Ignores the ./input directory and all files within it.
|
||||
-i Ignores the ../input directory and all files within it.
|
||||
-u Tells Autopsy not to ingest unallocated space.
|
||||
-k Keeps each image's Solr index instead of deleting it.
|
||||
-v Verbose mode; prints all errors to the screen.
|
||||
@ -1670,7 +1726,7 @@ def execute_test():
|
||||
global failedbool
|
||||
global html
|
||||
global attachl
|
||||
case.output_dir = make_path("output", time.strftime("%Y.%m.%d-%H.%M.%S"))
|
||||
case.output_dir = make_path("..", "output", "results", time.strftime("%Y.%m.%d-%H.%M.%S"))
|
||||
os.makedirs(case.output_dir)
|
||||
case.common_log = "AutopsyErrors.txt"
|
||||
case.csv = make_local_path(case.output_dir, "CSV.txt")
|
||||
@ -1696,7 +1752,7 @@ def execute_test():
|
||||
return
|
||||
run_test(args.single_file, 0)
|
||||
# If user has not selected a single file, and does not want to ignore
|
||||
# the input directory, continue on to parsing ./input
|
||||
# the input directory, continue on to parsing ../input
|
||||
if (not args.single) and (not args.ignore):
|
||||
for file in os.listdir(case.input_dir):
|
||||
# Make sure it's not a required hash/keyword file or dir
|
||||
@ -1720,7 +1776,8 @@ def execute_test():
|
||||
errorem = ""
|
||||
errorem += "There were no Errors.\n"
|
||||
attachl = []
|
||||
send_email()
|
||||
if not args.gold_creation:
|
||||
send_email()
|
||||
|
||||
|
||||
def send_email():
|
@ -40,6 +40,7 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JTextField;
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestCase;
|
||||
@ -51,6 +52,7 @@ import org.netbeans.jemmy.operators.JButtonOperator;
|
||||
import org.netbeans.jemmy.operators.JCheckBoxOperator;
|
||||
import org.netbeans.jemmy.operators.JDialogOperator;
|
||||
import org.netbeans.jemmy.operators.JFileChooserOperator;
|
||||
import org.netbeans.jemmy.operators.JLabelOperator;
|
||||
import org.netbeans.jemmy.operators.JTabbedPaneOperator;
|
||||
import org.netbeans.jemmy.operators.JTableOperator;
|
||||
import org.netbeans.jemmy.operators.JTextFieldOperator;
|
||||
@ -296,9 +298,24 @@ public class RegressionTest extends TestCase{
|
||||
new Timeout("pausing", 1000).sleep();
|
||||
JButtonOperator jbo1 = new JButtonOperator(reportDialogOperator, "Finish");
|
||||
jbo1.pushNoBlock();
|
||||
new Timeout("pausing", 8000).sleep(); // Give it a few seconds to generate
|
||||
screenshot("Progress");
|
||||
boolean waiting = true;
|
||||
new Timeout("pausing", 500).sleep();
|
||||
long size = 0;
|
||||
java.io.File rprtfldr = new java.io.File(System.getProperty("out_path")+java.io.File.separator+"AutopsyTestCase"+java.io.File.separator+"Reports"+java.io.File.separator+"AutopsyTestCase "+datenotime+java.io.File.separator+"HTML Report");
|
||||
JDialog previewDialog = JDialogOperator.waitJDialog("Progress", false, false);
|
||||
JLabel waiter = JLabelOperator.waitJLabel(previewDialog, "Complete", false, false);
|
||||
/*while(waiting)
|
||||
{
|
||||
new Timeout("pausing", 500).sleep();
|
||||
long currsize = size;
|
||||
size = 0;
|
||||
for(java.io.File elem: rprtfldr.listFiles())
|
||||
{
|
||||
size+=elem.getTotalSpace();
|
||||
}
|
||||
waiting = size>currsize;
|
||||
}*/
|
||||
screenshot("Progress");
|
||||
JDialogOperator previewDialogOperator = new JDialogOperator(previewDialog);
|
||||
JButtonOperator jbo2 = new JButtonOperator(previewDialogOperator, "Close");
|
||||
jbo2.pushNoBlock();
|
||||
|
@ -65,11 +65,8 @@ import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.netbeans.api.progress.ProgressHandle;
|
||||
import org.netbeans.api.progress.ProgressHandleFactory;
|
||||
@ -78,6 +75,7 @@ import org.openide.awt.ActionReference;
|
||||
import org.openide.awt.ActionReferences;
|
||||
import org.openide.awt.ActionRegistration;
|
||||
import org.openide.modules.InstalledFileLocator;
|
||||
import org.openide.modules.ModuleInstall;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
@ -141,6 +139,18 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar,
|
||||
private EventHandler fxMouseEnteredListener;
|
||||
private EventHandler fxMouseExitedListener;
|
||||
private SleuthkitCase skCase;
|
||||
private boolean fxInited = false;
|
||||
|
||||
public Timeline() {
|
||||
super();
|
||||
|
||||
org.sleuthkit.autopsy.core.Installer coreInstaller =
|
||||
ModuleInstall.findObject(org.sleuthkit.autopsy.core.Installer.class, false);
|
||||
if (coreInstaller != null) {
|
||||
fxInited = coreInstaller.isJavaFxInited();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Swing components and JavafX components don't play super well together
|
||||
//Swing components need to be initialized first, in the swing specific thread
|
||||
@ -165,7 +175,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar,
|
||||
@Override
|
||||
public void run() {
|
||||
//Making the main frame *
|
||||
|
||||
|
||||
mainFrame = new TimelineFrame();
|
||||
mainFrame.setFrameName(Case.getCurrentCase().getName() + " - Autopsy Timeline (Beta)");
|
||||
|
||||
@ -1019,7 +1029,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar,
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return Case.isCaseOpen();
|
||||
return Case.isCaseOpen() && this.fxInited;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,6 +1,6 @@
|
||||
#Updated by build script
|
||||
#Fri, 18 Jan 2013 11:06:14 -0500
|
||||
currentVersion=Autopsy 20130118
|
||||
#Tue, 26 Mar 2013 10:08:52 -0400
|
||||
currentVersion=Autopsy 20130326
|
||||
LBL_splash_window_title=Starting Autopsy
|
||||
SPLASH_HEIGHT=288
|
||||
SPLASH_WIDTH=538
|
||||
|
@ -1,4 +1,4 @@
|
||||
#Updated by build script
|
||||
#Fri, 18 Jan 2013 11:06:14 -0500
|
||||
CTL_MainWindow_Title=Autopsy 20130118
|
||||
CTL_MainWindow_Title_No_Project=Autopsy 20130118
|
||||
#Tue, 26 Mar 2013 10:08:52 -0400
|
||||
CTL_MainWindow_Title=Autopsy 20130326
|
||||
CTL_MainWindow_Title_No_Project=Autopsy 20130326
|
||||
|
16
build.xml
16
build.xml
@ -17,7 +17,7 @@
|
||||
<os family="windows"/>
|
||||
</condition>
|
||||
<import file="build-${os.family}.xml"/>
|
||||
|
||||
<property name="test-input" location="Testing/input"/>
|
||||
<property name="thirdparty.dir" value="${basedir}/thirdparty" />
|
||||
|
||||
<!-- import ant-contrib tools -->
|
||||
@ -239,4 +239,18 @@
|
||||
<target name="build-installer" depends="build-installer-dir" description="Builds Autopsy installer.">
|
||||
<antcall target="build-installer-${os.family}" />
|
||||
</target>
|
||||
<target name="test-get-imgs" description="Get test images and store them in the path represented by the test-input variable.">
|
||||
<available file="${test-input}/nps-2008-jean.E01" property="img-present"/>
|
||||
<if>
|
||||
<equals arg1="${img-present}" arg2="true"/>
|
||||
<then>
|
||||
<echo message = "Images already present."/>
|
||||
</then>
|
||||
<else>
|
||||
<mkdir dir="${test-input}" />
|
||||
<get src="http://digitalcorpora.org/corp/nps/drives/nps-2008-jean/nps-2008-jean.E01" dest="${test-input}"/>
|
||||
<get src="http://digitalcorpora.org/corp/nps/drives/nps-2008-jean/nps-2008-jean.E02" dest="${test-input}"/>
|
||||
</else>
|
||||
</if>
|
||||
</target>
|
||||
</project>
|
||||
|
Loading…
x
Reference in New Issue
Block a user