1
0
mirror of https://github.com/elisspace/autopsy.git synced 2026-09-06 02:24:30 +00:00

Merge branch 'master' of github.com:sleuthkit/autopsy

This commit is contained in:
Brian Carrier
2013-07-30 17:28:23 -04:00
17 changed files with 1677 additions and 1649 deletions

View File

@@ -56,7 +56,7 @@ import org.sleuthkit.datamodel.SleuthkitJNI.CaseDbHandle.AddImageProcess;
* open at a time. Use getCurrentCase() to retrieve the object for the current
* case.
*/
public class Case {
public class Case implements SleuthkitCase.ErrorObserver {
private static final String autopsyVer = Version.getVersion(); // current version of autopsy. Change it when the version is changed
private static final String appName = Version.getName() + " " + autopsyVer;
@@ -130,6 +130,7 @@ public class Case {
this.xmlcm = xmlcm;
this.db = db;
this.services = new Services(db);
db.addErrorObserver(this);
}
/**
@@ -983,4 +984,9 @@ public class Case {
CoreComponentControl.closeCoreWindows();
}
}
@Override
public void receiveError(String context, String errorMessage) {
MessageNotifyUtil.Notify.error(context, errorMessage);
}
}

View File

@@ -30,8 +30,8 @@ import org.sleuthkit.autopsy.coreutils.Logger;
*
* @author jantonius
*/
@ServiceProvider(service = CaseNewAction.class)
public final class CaseNewAction implements ActionListener {
@ServiceProvider(service = CaseNewActionInterface.class)
public final class CaseNewAction implements CaseNewActionInterface {
private NewCaseWizardAction wizard = SystemAction.get(NewCaseWizardAction.class);

View File

@@ -0,0 +1,30 @@
/*
* 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.casemodule;
import java.awt.event.ActionListener;
/**
*
*/
public interface CaseNewActionInterface extends ActionListener {
}

View File

@@ -1,4 +1,4 @@
<?xml version="1.1" encoding="UTF-8" ?>
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
@@ -36,7 +36,7 @@
<Component id="autopsyLogo" alignment="0" min="-2" pref="257" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="editorPanel" min="-2" max="-2" attributes="1"/>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="closeButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
@@ -65,16 +65,16 @@
<EmptySpace max="-2" attributes="0"/>
<Component id="createNewLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="openCaseButton" min="-2" max="-2" attributes="1"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="openLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="openRecentButton" min="-2" pref="70" max="-2" attributes="1"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="openRecentLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="openCaseButton" min="-2" max="-2" attributes="1"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="openLabel" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace pref="60" max="32767" attributes="0"/>
</Group>
@@ -83,37 +83,22 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="32" max="-2" attributes="0"/>
<Component id="newCaseButton" min="-2" pref="56" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/>
<Component id="createNewLabel" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="32" max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="createNewLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="newCaseButton" alignment="2" min="-2" pref="56" max="-2" attributes="0"/>
</Group>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="openCaseButton" min="-2" pref="58" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="31" max="-2" attributes="0"/>
<Component id="openLabel" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="openRecentLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="openRecentButton" alignment="2" min="-2" pref="70" max="-2" attributes="0"/>
</Group>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="openRecentButton" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="32" max="-2" attributes="0"/>
<Component id="openRecentLabel" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="openLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="openCaseButton" alignment="2" min="-2" pref="58" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
<EmptySpace pref="25" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@@ -140,30 +125,6 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newCaseButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="openCaseButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openCaseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="null" type="code"/>
</Property>
<Property name="borderPainted" type="boolean" value="false"/>
<Property name="contentAreaFilled" type="boolean" value="false"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[1, 1, 1, 1]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[70, 70]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openCaseButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="openRecentButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
@@ -195,16 +156,6 @@
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="openLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="13" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="openRecentLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
@@ -215,6 +166,40 @@
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="openCaseButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openCaseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="null" type="code"/>
</Property>
<Property name="borderPainted" type="boolean" value="false"/>
<Property name="contentAreaFilled" type="boolean" value="false"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[1, 1, 1, 1]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[70, 70]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openCaseButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="openLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="13" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="autopsyLogo">

View File

@@ -29,6 +29,7 @@ import javax.swing.JFrame;
import javax.swing.JPanel;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.coreutils.Version;
import org.netbeans.api.options.OptionsDisplayer;
/**
*
@@ -61,11 +62,11 @@ public class CueBannerPanel extends javax.swing.JPanel {
closeButton = new javax.swing.JButton();
editorPanel = new javax.swing.JPanel();
newCaseButton = new javax.swing.JButton();
openCaseButton = new javax.swing.JButton();
openRecentButton = new javax.swing.JButton();
createNewLabel = new javax.swing.JLabel();
openLabel = new javax.swing.JLabel();
openRecentLabel = new javax.swing.JLabel();
openCaseButton = new javax.swing.JButton();
openLabel = new javax.swing.JLabel();
autopsyLogo = new javax.swing.JLabel();
this.autopsyLogo.setText("");
@@ -83,6 +84,24 @@ public class CueBannerPanel extends javax.swing.JPanel {
}
});
openRecentButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_open_recent.png"))); // NOI18N
openRecentButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openRecentButton.text")); // NOI18N
openRecentButton.setBorder(null);
openRecentButton.setBorderPainted(false);
openRecentButton.setContentAreaFilled(false);
openRecentButton.setPreferredSize(new java.awt.Dimension(70, 70));
openRecentButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
openRecentButtonActionPerformed(evt);
}
});
createNewLabel.setFont(new java.awt.Font("Tahoma", 0, 13)); // NOI18N
createNewLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.createNewLabel.text")); // NOI18N
openRecentLabel.setFont(new java.awt.Font("Tahoma", 0, 13)); // NOI18N
openRecentLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openRecentLabel.text")); // NOI18N
openCaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"))); // NOI18N
openCaseButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openCaseButton.text")); // NOI18N
openCaseButton.setBorder(null);
@@ -96,27 +115,9 @@ public class CueBannerPanel extends javax.swing.JPanel {
}
});
openRecentButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_open_recent.png"))); // NOI18N
openRecentButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openRecentButton.text")); // NOI18N
openRecentButton.setBorder(null);
openRecentButton.setBorderPainted(false);
openRecentButton.setContentAreaFilled(false);
openRecentButton.setPreferredSize(new java.awt.Dimension(70, 70));
openRecentButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
openRecentButtonActionPerformed(evt);
}
});
createNewLabel.setFont(new java.awt.Font("Tahoma", 0, 13));
createNewLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.createNewLabel.text")); // NOI18N
openLabel.setFont(new java.awt.Font("Tahoma", 0, 13));
openLabel.setFont(new java.awt.Font("Tahoma", 0, 13)); // NOI18N
openLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openLabel.text")); // NOI18N
openRecentLabel.setFont(new java.awt.Font("Tahoma", 0, 13));
openRecentLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openRecentLabel.text")); // NOI18N
javax.swing.GroupLayout editorPanelLayout = new javax.swing.GroupLayout(editorPanel);
editorPanel.setLayout(editorPanelLayout);
editorPanelLayout.setHorizontalGroup(
@@ -127,41 +128,32 @@ public class CueBannerPanel extends javax.swing.JPanel {
.addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(createNewLabel))
.addGroup(editorPanelLayout.createSequentialGroup()
.addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(openLabel))
.addGroup(editorPanelLayout.createSequentialGroup()
.addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(openRecentLabel)))
.addComponent(openRecentLabel))
.addGroup(editorPanelLayout.createSequentialGroup()
.addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(openLabel)))
.addContainerGap(60, Short.MAX_VALUE))
);
editorPanelLayout.setVerticalGroup(
editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(editorPanelLayout.createSequentialGroup()
.addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(editorPanelLayout.createSequentialGroup()
.addGap(32, 32, 32)
.addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(editorPanelLayout.createSequentialGroup()
.addGap(50, 50, 50)
.addComponent(createNewLabel)))
.addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(editorPanelLayout.createSequentialGroup()
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(editorPanelLayout.createSequentialGroup()
.addGap(31, 31, 31)
.addComponent(openLabel)))
.addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(editorPanelLayout.createSequentialGroup()
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(editorPanelLayout.createSequentialGroup()
.addGap(32, 32, 32)
.addComponent(openRecentLabel)))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGap(32, 32, 32)
.addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(createNewLabel)
.addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(openRecentLabel)
.addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(0, 0, Short.MAX_VALUE)
.addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(openLabel)
.addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(25, Short.MAX_VALUE))
);
autopsyLogo.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/welcome_logo.png"))); // NOI18N
@@ -188,14 +180,14 @@ public class CueBannerPanel extends javax.swing.JPanel {
.addComponent(autopsyLogo, javax.swing.GroupLayout.PREFERRED_SIZE, 257, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(layout.createSequentialGroup()
.addComponent(editorPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGap(18, 18, 18)
.addComponent(closeButton)))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
private void newCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newCaseButtonActionPerformed
Lookup.getDefault().lookup(CaseNewAction.class).actionPerformed(evt);
Lookup.getDefault().lookup(CaseNewActionInterface.class).actionPerformed(evt);
}//GEN-LAST:event_newCaseButtonActionPerformed
private void openCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openCaseButtonActionPerformed
@@ -255,6 +247,15 @@ public class CueBannerPanel extends javax.swing.JPanel {
closeButton.addActionListener(e);
}
/**
* Sets the Close button label (default is "Close").
*
* @param text The new label for the button.
*/
public void setCloseButtonText(String text) {
closeButton.setText(text);
}
/**
* Close the open recent cases window.
*/

View File

@@ -43,6 +43,7 @@ public class IngestMonitor {
private Timer timer;
private static final java.util.logging.Logger MONITOR_LOGGER = java.util.logging.Logger.getLogger("monitor");
private MonitorAction monitor;
public static final int DISK_FREE_SPACE_UNKNOWN = -1;
IngestMonitor() {
@@ -101,7 +102,7 @@ public class IngestMonitor {
return monitor.getFreeSpace();
} catch (SecurityException e) {
logger.log(Level.WARNING, "Error checking for free disk space on ingest data drive", e);
return -1;
return DISK_FREE_SPACE_UNKNOWN;
}
}
@@ -109,7 +110,7 @@ public class IngestMonitor {
private class MonitorAction implements ActionListener {
private final static long MIN_FREE_DISK_SPACE = 100L * 1024 * 1024; //100MB
private File root = new File(File.separator); //default, roto dir where autopsy runs
private File root = new File(File.separator); //default, root dir where autopsy runs
MonitorAction() {
//find drive where case is located
@@ -170,12 +171,25 @@ public class IngestMonitor {
}
/**
* Get free space in bytes of the drive where case dir resides
* Get free space in bytes of the drive where case dir resides, or -1 if
* unknown
*
* @return free space in bytes
*/
private long getFreeSpace() throws SecurityException {
return root.getFreeSpace();
final long freeSpace = root.getFreeSpace();
if (freeSpace == 0) {
//check if network drive, some network filesystems always return 0
final String monitoredPath = root.getAbsolutePath();
if (monitoredPath.startsWith("\\\\") || monitoredPath.startsWith("//")) {
return DISK_FREE_SPACE_UNKNOWN;
}
}
return freeSpace;
}
/**
@@ -191,8 +205,13 @@ public class IngestMonitor {
logger.log(Level.WARNING, "Unable to check for free disk space (permission issue)", e);
return true; //OK
}
//logger.log(Level.INFO, "Checking free disk apce: " + freeSpace + " need: " + Long.toString(MIN_FREE_DISK_SPACE));
return freeSpace > MIN_FREE_DISK_SPACE;
if (freeSpace == DISK_FREE_SPACE_UNKNOWN) {
return true;
} else {
//logger.log(Level.INFO, "Checking free disk apce: " + freeSpace + " need: " + Long.toString(MIN_FREE_DISK_SPACE));
return freeSpace > MIN_FREE_DISK_SPACE;
}
}
/**

View File

@@ -399,10 +399,10 @@ public class ReportHTML implements TableReportModule {
try {
AbstractFile file = Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(sourceArtifact.getObjectID());
// Don't make a local copy of the file if it is unallocated space or a virtual directory.
if (file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS ||
file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS ||
file.getType() == TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR) {
// Don't make a local copy of the file if it is a directory or unallocated space.
if (file.isDir() ||
file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS ||
file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) {
row.add("");
return;
}

View File

@@ -2,7 +2,7 @@
*
* Autopsy Forensic Browser
*
* Copyright 2012 Basis Technology Corp.
* Copyright 2013 Basis Technology Corp.
*
* Copyright 2012 42six Solutions.
* Contact: aebadirad <at> 42six <dot> com
@@ -30,7 +30,6 @@ import java.beans.PropertyChangeListener;
import java.io.File;
import java.text.MessageFormat;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import javax.swing.ImageIcon;
import javax.swing.JButton;
@@ -59,47 +58,12 @@ public final class ReportWizardAction extends CallableSystemAction implements P
private JButton toolbarButton = new JButton();
private static final String ACTION_NAME = "Generate Report";
public ReportWizardAction() {
setEnabled(false);
Case.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(Case.CASE_CURRENT_CASE)) {
Case newCase = (Case) evt.getNewValue();
setEnabled(newCase != null);
// Make the cases' Reoports folder, if it doesn't exist
if (newCase != null) {
boolean exists = (new File(newCase.getCaseDirectory() + File.separator + "Reports")).exists();
if (!exists) {
boolean reportCreate = (new File(newCase.getCaseDirectory() + File.separator + "Reports")).mkdirs();
if (!reportCreate) {
logger.log(Level.WARNING, "Could not create Reports directory for case. It does not exist.");
}
}
}
}
}
});
// Initialize the Generate Report button
toolbarButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ReportWizardAction.this.actionPerformed(e);
}
});
}
/**
* When the Generate Report button or menu item is selected, open the reporting wizard.
* When the wizard is finished, create a ReportGenerator with the wizard information,
* and start all necessary reports.
*/
@Override
@SuppressWarnings("unchecked")
public void actionPerformed(ActionEvent e) {
public static void doReportWizard() {
// Create the wizard
WizardDescriptor wiz = new WizardDescriptor(new ReportWizardIterator());
wiz.setTitleFormat(new MessageFormat("{0} {1}"));
@@ -133,7 +97,45 @@ public final class ReportWizardAction extends CallableSystemAction implements P
// Open the progress window for the user
generator.displayProgressPanels();
}
}
}
public ReportWizardAction() {
setEnabled(false);
Case.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(Case.CASE_CURRENT_CASE)) {
Case newCase = (Case) evt.getNewValue();
setEnabled(newCase != null);
// Make the cases' Reoports folder, if it doesn't exist
if (newCase != null) {
boolean exists = (new File(newCase.getCaseDirectory() + File.separator + "Reports")).exists();
if (!exists) {
boolean reportCreate = (new File(newCase.getCaseDirectory() + File.separator + "Reports")).mkdirs();
if (!reportCreate) {
logger.log(Level.WARNING, "Could not create Reports directory for case. It does not exist.");
}
}
}
}
}
});
// Initialize the Generate Report button
toolbarButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ReportWizardAction.this.actionPerformed(e);
}
});
}
@Override
@SuppressWarnings("unchecked")
public void actionPerformed(ActionEvent e) {
doReportWizard();
}
@Override

View File

@@ -49,6 +49,7 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.ingest.PipelineContext;
import org.sleuthkit.autopsy.ingest.IngestMessage;
import org.sleuthkit.autopsy.ingest.IngestMonitor;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
@@ -426,7 +427,7 @@ public final class SevenZipIngestModule extends IngestModuleAbstractFile {
//check if unpacking this file will result in out of disk space
//this is additional to zip bomb prevention mechanism
if (freeDiskSpace != -1 && size > 0) { //if known free space and file not empty
if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && size > 0) { //if known free space and file not empty
long newDiskSpace = freeDiskSpace - size;
if (newDiskSpace < MIN_FREE_DISK_SPACE) {
String msg = "Not enough disk space to unpack archive item: " + archiveFile.getName() + ", " + fileName;

View File

@@ -1,5 +1,5 @@
#Updated by build script
#Sun, 02 Jun 2013 00:12:29 -0400
#Thu, 25 Jul 2013 15:34:25 -0400
LBL_splash_window_title=Starting Autopsy
SPLASH_HEIGHT=288
SPLASH_WIDTH=538
@@ -8,4 +8,4 @@ SplashRunningTextBounds=5,266,530,17
SplashRunningTextColor=0x0
SplashRunningTextFontSize=18
currentVersion=Autopsy 20130602
currentVersion=Autopsy 20130725

View File

@@ -1,5 +1,5 @@
#Updated by build script
#Thu, 23 May 2013 00:04:58 -0400
#Thu, 25 Jul 2013 15:34:25 -0400
CTL_MainWindow_Title=Autopsy 20130523
CTL_MainWindow_Title_No_Project=Autopsy 20130523
CTL_MainWindow_Title=Autopsy 20130725
CTL_MainWindow_Title_No_Project=Autopsy 20130725

View File

@@ -16,7 +16,7 @@
# 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.
from tskdbdiff import TskDbDiff, TskDbDiffException
import codecs
import datetime
import logging
@@ -190,7 +190,8 @@ class TestRunner(object):
try:
# Dump the database before we diff or use it for rebuild
TskDbDiff.dump_output_db(test_data)
TskDbDiff.dump_output_db(test_data.get_db_path(DBType.OUTPUT), test_data.get_db_dump_path(DBType.OUTPUT),
test_data.get_sorted_data_path(DBType.OUTPUT))
except sqlite3.OperationalError as e:
print("Ingest did not run properly.",
"Make sure no other instances of Autopsy are open and try again.")
@@ -221,8 +222,7 @@ class TestRunner(object):
TestResultsDiffer.run_diff(test_data)
test_data.overall_passed = (test_data.html_report_passed and
test_data.errors_diff_passed and test_data.sorted_data_passed and
test_data.db_dump_passed and test_data.db_diff_results.passed)
test_data.errors_diff_passed and test_data.db_diff_passed)
Reports.generate_reports(test_data)
if(not test_data.overall_passed):
@@ -396,10 +396,9 @@ class TestData(object):
gold_archive: a pathto_File, the gold standard archive
logs_dir: a pathto_Dir, the location where autopsy logs are stored
solr_index: a pathto_Dir, the locatino of the solr index
db_diff_results: a DiffResults, the results of the database comparison
html_report_passed: a boolean, did the HTML report diff pass?
errors_diff_passed: a boolean, did the error diff pass?
db_dump_passed: a boolean, did the db dump diff pass?
db_diff_passed: a boolean, did the db diff pass?
overall_passed: a boolean, did the test pass?
total_test_time: a String representation of the test duration
start_date: a String representation of this TestData's start date
@@ -448,11 +447,9 @@ class TestData(object):
self.solr_index = make_path(self.output_path, AUTOPSY_TEST_CASE,
"ModuleOutput", "KeywordSearch")
# Results and Info
self.db_diff_results = None
self.html_report_passed = False
self.errors_diff_passed = False
self.sorted_data_passed = False
self.db_dump_passed = False
self.db_diff_passed = False
self.overall_passed = False
# Ingest info
self.total_test_time = ""
@@ -603,7 +600,7 @@ class TestConfiguration(object):
timer = 0
self.images = []
# Email info
self.email_enabled = False
self.email_enabled = args.email_enabled
self.mail_server = ""
self.mail_to = ""
self.mail_subject = ""
@@ -713,356 +710,12 @@ class TestConfiguration(object):
if subject_elements:
subject = subject_elements[0]
self.mail_subject = subject.getAttribute("value").encode().decode("utf_8")
if self.mail_server and self.mail_to:
if self.mail_server and self.mail_to and self.args.email_enabled:
self.email_enabled = True
class TskDbDiff(object):
"""Represents the differences between the gold and output databases.
Contains methods to compare two databases.
Attributes:
gold_artifacts:
autopsy_artifacts:
gold_attributes:
autopsy_attributes:
gold_objects:
autopsy_objects:
artifact_comparison:
attribute_comparision:
report_errors: a listof_listof_String, the error messages that will be
printed to screen in the run_diff method
passed: a boolean, did the diff pass?
autopsy_db_file:
gold_db_file:
"""
def __init__(self, output_db_path, gold_db_path):
"""Constructor for TskDbDiff.
Args:
output_db_path: a pathto_File, the output database
gold_db_path: a pathto_File, the gold database
"""
self.gold_artifacts = []
self.autopsy_artifacts = []
self.gold_attributes = 0
self.autopsy_attributes = 0
self.gold_objects = 0
self.autopsy_objects = 0
self.artifact_comparison = []
self.attribute_comparison = []
self.report_errors = []
self.autopsy_db_file = output_db_path
self.gold_db_file = gold_db_path
def _get_artifacts(self, cursor):
"""Get a list of artifacts from the given SQLCursor.
Args:
cursor: SQLCursor - the cursor to execute on
Returns:
listof_Artifact - the artifacts found by the query
"""
cursor.execute("SELECT COUNT(*) FROM blackboard_artifact_types")
length = cursor.fetchone()[0] + 1
artifacts = []
for type_id in range(1, length):
cursor.execute("SELECT COUNT(*) FROM blackboard_artifacts WHERE artifact_type_id=%d" % type_id)
artifacts.append(cursor.fetchone()[0])
return artifacts
def _count_attributes(self, cursor):
"""Count the attributes from the given SQLCursor.
Args:
cursor: SQLCursor - the cursor to execute on
Returns:
Nat - the number of attributes found by the query
"""
cursor.execute("SELECT COUNT(*) FROM blackboard_attributes")
return cursor.fetchone()[0]
def _count_objects(self, cursor):
"""Count the objects from the given SQLCursor.
Args:
cursor: SQLCursor - the cursor to execute on
Returns:
Nat - the number of objects found by the query
"""
cursor.execute("SELECT COUNT(*) FROM tsk_objects")
return cursor.fetchone()[0]
def _compare_bb_artifacts(self):
"""Compares the blackboard artifact counts of two databases.
Returns:
True if the artifacts are the same, false otherwise.
"""
exceptions = []
passed = True
if self.gold_artifacts != self.autopsy_artifacts:
msg = "There was a difference in the number of artifacts.\n"
exceptions.append(msg)
Errors.add_email_msg(msg)
passed = False
print("Email will be sent to ", self.mail_to)
else:
rner = len(self.gold_artifacts)
for type_id in range(1, rner):
if self.gold_artifacts[type_id] != self.autopsy_artifacts[type_id]:
error = str("Artifact counts do not match for type id %d. " % type_id)
error += str("Gold: %d, Test: %d" %
(self.gold_artifacts[type_id],
self.autopsy_artifacts[type_id]))
exceptions.append(error)
passed = False
self.report_errors.append(exceptions)
return passed
print("No email will be sent.")
def _compare_bb_attributes(self):
"""Compares the blackboard attribute counts of two databases.
Updates this TskDbDiff's report_errors with the error messages from the
attribute diff
Returns:
True is the attributes are the same, False otherwise.
"""
exceptions = []
passed = True
if self.gold_attributes != self.autopsy_attributes:
error = "Attribute counts do not match. "
error += str("Gold: %d, Test: %d" % (self.gold_attributes, self.autopsy_attributes))
exceptions.append(error)
msg = "There was a difference in the number of attributes.\n"
Errors.add_email_msg(msg)
passed = False
self.report_errors.append(exceptions)
return passed
def _compare_tsk_objects(self):
"""Compares the TSK object counts of two databases."""
exceptions = []
passed = True
if self.gold_objects != self.autopsy_objects:
error = "TSK Object counts do not match. "
error += str("Gold: %d, Test: %d" % (self.gold_objects, self.autopsy_objects))
exceptions.append(error)
msg ="There was a difference between the tsk object counts.\n"
Errors.add_email_msg(msg)
passed = False
self.report_errors.append(exceptions)
return passed
def _get_basic_counts(self, autopsy_cur, gold_cur):
"""Count the items necessary to compare the databases.
Gets the counts of objects, artifacts, and attributes in the Gold
and Ouput databases and updates this TskDbDiff's attributes
accordingly
Args:
autopsy_cur: SQLCursor - the cursor for the output database
gold_cur: SQLCursor - the cursor for the gold database
"""
try:
# Objects
self.gold_objects = self._count_objects(gold_cur)
self.autopsy_objects = self._count_objects(autopsy_cur)
# Artifacts
self.gold_artifacts = self._get_artifacts(gold_cur)
self.autopsy_artifacts = self._get_artifacts(autopsy_cur)
# Attributes
self.gold_attributes = self._count_attributes(gold_cur)
self.autopsy_attributes = self._count_attributes(autopsy_cur)
except sqlite3.Error as e:
Errors.print_error("Error while querying the databases:" + str(e))
def run_diff(self):
"""Basic test between output and gold databases.
Compares only counts of objects and blackboard items.
Note: SQLITE needs unix style pathing
Raises:
sqlite3.OperationalError, if either of the database files do not
exist
"""
# Get connections and cursors to output / gold databases
autopsy_con = sqlite3.connect(self.autopsy_db_file)
autopsy_cur = autopsy_con.cursor()
gold_con = sqlite3.connect(self.gold_db_file)
gold_cur = gold_con.cursor()
# Get Counts of objects, artifacts, and attributes
self._get_basic_counts(autopsy_cur, gold_cur)
# We're done with the databases, close up the connections
autopsy_con.close()
gold_con.close()
# Compare counts
objects_passed = self._compare_tsk_objects()
artifacts_passed = self._compare_bb_artifacts()
attributes_passed = self._compare_bb_attributes()
self.passed = objects_passed and artifacts_passed and attributes_passed
self.artifact_comparison = self.report_errors[1]
self.attribute_comparison = self.report_errors[2]
okay = "All counts match."
print_report(self.report_errors[0], "COMPARE TSK OBJECTS", okay)
print_report(self.report_errors[1], "COMPARE ARTIFACTS", okay)
print_report(self.report_errors[2], "COMPARE ATTRIBUTES", okay)
return DiffResults(self)
def _dump_output_db_bb(autopsy_con, db_file, data_file, sorted_data_file):
"""Dumps sorted text results to the given output location.
Smart method that deals with a blackboard comparison to avoid issues
with different IDs based on when artifacts were created.
Args:
autopsy_con: a SQLConn to the autopsy database.
db_file: a pathto_File, the output database.
data_file: a pathto_File, the dump file to write to
sorted_data_file: a pathto_File, the sorted dump file to write to
"""
autopsy_cur2 = autopsy_con.cursor()
# Get the list of all artifacts
# @@@ Could add a SORT by parent_path in here since that is how we are going to later sort it.
autopsy_cur2.execute("SELECT tsk_files.parent_path, tsk_files.name, blackboard_artifact_types.display_name, blackboard_artifacts.artifact_id FROM blackboard_artifact_types INNER JOIN blackboard_artifacts ON blackboard_artifact_types.artifact_type_id = blackboard_artifacts.artifact_type_id INNER JOIN tsk_files ON tsk_files.obj_id = blackboard_artifacts.obj_id")
database_log = codecs.open(data_file, "wb", "utf_8")
rw = autopsy_cur2.fetchone()
appnd = False
counter = 0
artifact_count = 0
artifact_fail = 0
# Cycle through artifacts
try:
while (rw != None):
# File Name and artifact type
if(rw[0] != None):
database_log.write(rw[0] + rw[1] + ' <artifact type="' + rw[2] + '" > ')
else:
database_log.write(rw[1] + ' <artifact type="' + rw[2] + '" > ')
# Get attributes for this artifact
autopsy_cur1 = autopsy_con.cursor()
looptry = True
artifact_count += 1
try:
key = ""
key = str(rw[3])
key = key,
autopsy_cur1.execute("SELECT blackboard_attributes.source, blackboard_attribute_types.display_name, blackboard_attributes.value_type, blackboard_attributes.value_text, blackboard_attributes.value_int32, blackboard_attributes.value_int64, blackboard_attributes.value_double FROM blackboard_attributes INNER JOIN blackboard_attribute_types ON blackboard_attributes.attribute_type_id = blackboard_attribute_types.attribute_type_id WHERE artifact_id =? ORDER BY blackboard_attributes.source, blackboard_attribute_types.display_name, blackboard_attributes.value_type, blackboard_attributes.value_text, blackboard_attributes.value_int32, blackboard_attributes.value_int64, blackboard_attributes.value_double", key)
attributes = autopsy_cur1.fetchall()
except sqlite3.Error as e:
Errors.print_error(str(e))
Errors.print_error(str(rw[3]))
msg ="Attributes in artifact id (in output DB)# " + str(rw[3]) + " encountered an error: " + str(e) +" .\n"
Errors.add_email_msg(msg)
looptry = False
print(artifact_fail)
artifact_fail += 1
print(artifact_fail)
database_log.write('Error Extracting Attributes');
# Print attributes
if(looptry == True):
src = attributes[0][0]
for attr in attributes:
val = 3 + attr[2]
numvals = 0
for x in range(3, 6):
if(attr[x] != None):
numvals += 1
if(numvals > 1):
msg = "There were too many values for attribute type: " + attr[1] + " for artifact with id #" + str(rw[3]) + ".\n"
Errors.add_email_msg(msg)
Errors.print_error(msg)
if(not appnd):
Errors.add_email_attachment(db_file)
appnd = True
if(not attr[0] == src):
msg ="There were inconsistent sources for artifact with id #" + str(rw[3]) + ".\n"
Errors.add_email_msg(msg)
Errors.print_error(msg)
if(not appnd):
Errors.add_email_attachment(db_file)
appnd = True
try:
database_log.write('<attribute source="' + attr[0] + '" type="' + attr[1] + '" value="')
inpval = attr[val]
if((type(inpval) != 'unicode') or (type(inpval) != 'str')):
inpval = str(inpval)
patrn = re.compile("[\n\0\a\b\r\f\e]")
inpval = re.sub(patrn, ' ', inpval)
database_log.write(inpval)
except IOError as e:
Errors.print_error(str(e))
database_log.write('" />')
database_log.write(' <artifact/>\n')
rw = autopsy_cur2.fetchone()
# Now sort the file
srtcmdlst = ["sort", data_file, "-o", sorted_data_file]
subprocess.call(srtcmdlst)
print(artifact_fail)
if(artifact_fail > 0):
msg ="There were " + str(artifact_count) + " artifacts and " + str(artifact_fail) + " threw an exception while loading.\n"
Errors.add_email_msg(msg)
except Exception as e:
Errors.print_error('outer exception: ' + str(e))
def _dump_output_db_nonbb(test_data):
"""Dumps a database to a text file.
Does not dump the artifact and attributes.
Args:
test_data: the TestData that corresponds with this dump.
"""
# Make a copy of the DB
autopsy_db_file = test_data.get_db_path(DBType.OUTPUT)
backup_db_file = test_data.get_db_path(DBType.BACKUP)
shutil.copy(autopsy_db_file, backup_db_file)
autopsy_con = sqlite3.connect(backup_db_file)
# Delete the blackboard tables
autopsy_con.execute("DROP TABLE blackboard_artifacts")
autopsy_con.execute("DROP TABLE blackboard_attributes")
# Write to the database dump
with codecs.open(test_data.test_dbdump, "wb", "utf_8") as db_log:
for line in autopsy_con.iterdump():
db_log.write('%s\n' %line)
def dump_output_db(test_data):
"""Dumps the given database to text files for later comparison.
Args:
test_data: the TestData that corresponds to this dump.
"""
autopsy_db_file = test_data.get_db_path(DBType.OUTPUT)
autopsy_con = sqlite3.connect(autopsy_db_file)
autopsy_cur = autopsy_con.cursor()
# Try to query the databases. Ignore any exceptions, the function will
# return an error later on if these do fail
TskDbDiff._dump_output_db_bb(autopsy_con, autopsy_db_file,
test_data.autopsy_data_file,
test_data.get_sorted_data_path(DBType.OUTPUT))
TskDbDiff._dump_output_db_nonbb(test_data)
autopsy_con.close()
#-------------------------------------------------#
# Functions relating to comparing outputs #
@@ -1078,11 +731,13 @@ class TestResultsDiffer(object):
databaseDiff: TskDbDiff object created based off test_data
"""
try:
# Diff the gold and output databases
output_db_path = test_data.get_db_path(DBType.OUTPUT)
gold_db_path = test_data.get_db_path(DBType.GOLD)
db_diff = TskDbDiff(output_db_path, gold_db_path)
test_data.db_diff_results = db_diff.run_diff()
output_db = test_data.get_db_path(DBType.OUTPUT)
gold_db = test_data.get_db_path(DBType.GOLD)
output_dir = test_data.output_path
gold_bb_dump = test_data.get_sorted_data_path(DBType.GOLD)
gold_dump = test_data.get_db_dump_path(DBType.GOLD)
test_data.db_diff_pass = all(TskDbDiff(output_db, gold_db, output_dir=output_dir, gold_bb_dump=gold_bb_dump,
gold_dump=gold_dump).run_diff())
# Compare Exceptions
# replace is a fucntion that replaces strings of digits with 'd'
@@ -1094,18 +749,6 @@ class TestResultsDiffer(object):
replace)
test_data.errors_diff_passed = passed
# Compare smart blackboard results
output_data = test_data.get_sorted_data_path(DBType.OUTPUT)
gold_data = test_data.get_sorted_data_path(DBType.GOLD)
passed = TestResultsDiffer._compare_text(output_data, gold_data)
test_data.sorted_data_passed = passed
# Compare the rest of the database (non-BB)
output_dump = test_data.get_db_dump_path(DBType.OUTPUT)
gold_dump = test_data.get_db_dump_path(DBType.GOLD)
passed = TestResultsDiffer._compare_text(output_dump, gold_dump)
test_data.db_dump_passed = passed
# Compare html output
gold_report_path = test_data.get_html_report_path(DBType.GOLD)
output_report_path = test_data.get_html_report_path(DBType.OUTPUT)
@@ -1119,6 +762,8 @@ class TestResultsDiffer(object):
except sqlite3.OperationalError as e:
Errors.print_error("Tests failed while running the diff:\n")
Errors.print_error(str(e))
except TskDbDiffException as e:
Errors.print_error(str(e))
except Exception as e:
Errors.print_error("Tests failed due to an error, try rebuilding or creating gold standards.\n")
Errors.print_error(str(e) + "\n")
@@ -1339,14 +984,14 @@ class Reports(object):
info += "<tr><td>Out Of Disk Space:\
<p style='font-size: 11px;'>(will skew other test results)</p></td>"
info += "<td>" + str(len(search_log_set("autopsy", "Stopping ingest due to low disk space on disk", test_data))) + "</td></tr>"
info += "<tr><td>TSK Objects Count:</td>"
info += "<td>" + str(test_data.db_diff_results.output_objs) + "</td></tr>"
info += "<tr><td>Artifacts Count:</td>"
info += "<td>" + str(test_data.db_diff_results.output_artifacts)+ "</td></tr>"
info += "<tr><td>Attributes Count:</td>"
info += "<td>" + str(test_data.db_diff_results.output_attrs) + "</td></tr>"
# info += "<tr><td>TSK Objects Count:</td>"
# info += "<td>" + str(test_data.db_diff_results.output_objs) + "</td></tr>"
# info += "<tr><td>Artifacts Count:</td>"
# info += "<td>" + str(test_data.db_diff_results.output_artifacts)+ "</td></tr>"
# info += "<tr><td>Attributes Count:</td>"
# info += "<td>" + str(test_data.db_diff_results.output_attrs) + "</td></tr>"
info += "</table>\
</div>"
</div>"
# For all the general print statements in the test_config
output = "<div id='general'>\
<h2><a name='" + test_data.image_name + "-general'>General Output</a></h2>\
@@ -1453,12 +1098,12 @@ class Reports(object):
vars.append( str(test_data.indexed_files) )
vars.append( str(test_data.indexed_chunks) )
vars.append( str(len(search_log_set("autopsy", "Stopping ingest due to low disk space on disk", test_data))) )
vars.append( str(test_data.db_diff_results.output_objs) )
vars.append( str(test_data.db_diff_results.output_artifacts) )
vars.append( str(test_data.db_diff_results.output_objs) )
# vars.append( str(test_data.db_diff_results.output_objs) )
# vars.append( str(test_data.db_diff_results.output_artifacts) )
# vars.append( str(test_data.db_diff_results.output_objs) )
vars.append( make_local_path("gold", test_data.image_name, DB_FILENAME) )
vars.append( test_data.db_diff_results.get_artifact_comparison() )
vars.append( test_data.db_diff_results.get_attribute_comparison() )
# vars.append( test_data.db_diff_results.get_artifact_comparison() )
# vars.append( test_data.db_diff_results.get_attribute_comparison() )
vars.append( make_local_path("gold", test_data.image_name, "standard.html") )
vars.append( str(test_data.html_report_passed) )
vars.append( test_data.ant_to_string() )
@@ -1493,12 +1138,12 @@ class Reports(object):
titles.append("Indexed Files Count")
titles.append("Indexed File Chunks Count")
titles.append("Out Of Disk Space")
titles.append("Tsk Objects Count")
titles.append("Artifacts Count")
titles.append("Attributes Count")
# titles.append("Tsk Objects Count")
# titles.append("Artifacts Count")
# titles.append("Attributes Count")
titles.append("Gold Database Name")
titles.append("Artifacts Comparison")
titles.append("Attributes Comparison")
# titles.append("Artifacts Comparison")
# titles.append("Attributes Comparison")
titles.append("Gold Report Name")
titles.append("Report Comparison")
titles.append("Ant Command Line")
@@ -1951,6 +1596,7 @@ class Args(object):
self.exception = False
self.exception_string = ""
self.fr = False
self.email_enabled = False
def parse(self):
"""Get the command line arguments and parse them."""
@@ -2010,6 +1656,8 @@ class Args(object):
elif arg == "-fr" or arg == "--forcerun":
print("Not downloading new images")
self.fr = True
elif arg == "-e" or arg == "-email":
self.email_enabled = True
else:
print(usage())
return False

View File

@@ -152,3 +152,4 @@ def get_files_by_ext(dir_path, ext):
"""
return [ os.path.join(dir_path, file) for file in os.listdir(dir_path) if
file.endswith(ext) ]

View File

@@ -1,186 +1,187 @@
import codecs
import datetime
import logging
import os
import re
import shutil
import socket
import sqlite3
import subprocess
import sys
from sys import platform as _platform
import time
import traceback
import xml
from xml.dom.minidom import parse, parseString
import Emailer
def compile(errore, attachli, parsedin):
global redo
global tryredo
global failedbool
global errorem
errorem = errore
global attachl
attachl = attachli
global passed
global parsed
parsed = parsedin
passed = True
tryredo = False
redo = True
while(redo):
passed = True
if(passed):
gitPull("sleuthkit")
if(passed):
vsBuild()
if(passed):
gitPull("autopsy")
if(passed):
antBuild("datamodel", False)
if(passed):
antBuild("autopsy", True)
if(passed):
redo = False
else:
print("Compile Failed")
time.sleep(3600)
attachl = []
errorem = "The test standard didn't match the gold standard.\n"
failedbool = False
if(tryredo):
errorem = ""
errorem += "Rebuilt properly.\n"
Emailer.send_email(parsed, errorem, attachl, True)
attachl = []
passed = True
#Pulls from git
def gitPull(TskOrAutopsy):
global SYS
global errorem
global attachl
ccwd = ""
gppth = Emailer.make_local_path("..", "GitPullOutput" + TskOrAutopsy + ".txt")
attachl.append(gppth)
gpout = open(gppth, 'a')
toPull = "https://www.github.com/sleuthkit/" + TskOrAutopsy
call = ["git", "pull", toPull]
if TskOrAutopsy == "sleuthkit":
ccwd = os.path.join("..", "..", "..", "sleuthkit")
else:
ccwd = os.path.join("..", "..")
subprocess.call(call, stdout=sys.stdout, cwd=ccwd)
gpout.close()
#Builds TSK as a win32 applicatiion
def vsBuild():
global redo
global tryredo
global passed
global parsed
#Please ensure that the current working directory is $autopsy/testing/script
oldpath = os.getcwd()
os.chdir(os.path.join("..", "..", "..","sleuthkit", "win32"))
vs = []
vs.append("/cygdrive/c/windows/microsoft.NET/framework/v4.0.30319/MSBuild.exe")
vs.append(os.path.join("Tsk-win.sln"))
vs.append("/p:configuration=release")
vs.append("/p:platform=win32")
vs.append("/t:clean")
vs.append("/t:rebuild")
print(vs)
VSpth = Emailer.make_local_path("..", "VSOutput.txt")
VSout = open(VSpth, 'a')
subprocess.call(vs, stdout=VSout)
VSout.close()
os.chdir(oldpath)
chk = os.path.join("..", "..", "..","sleuthkit", "win32", "Release", "libtsk_jni.dll")
try:
open(chk)
except IOError as e:
global errorem
global attachl
if(not tryredo):
errorem += "LIBTSK C++ failed to build.\n"
attachl.append(VSpth)
Emailer.send_email(parsed, errorem, attachl, False)
tryredo = True
passed = False
redo = True
#Builds Autopsy or the Datamodel
def antBuild(which, Build):
global redo
global passed
global tryredo
global parsed
directory = os.path.join("..", "..")
ant = []
if which == "datamodel":
directory = os.path.join("..", "..", "..", "sleuthkit", "bindings", "java")
ant.append("ant")
ant.append("-f")
ant.append(directory)
ant.append("clean")
if(Build):
ant.append("build")
else:
ant.append("dist")
antpth = Emailer.make_local_path("..", "ant" + which + "Output.txt")
antout = open(antpth, 'a')
succd = subprocess.call(ant, stdout=antout)
antout.close()
global errorem
global attachl
if which == "datamodel":
chk = os.path.join("..", "..", "..","sleuthkit", "bindings", "java", "dist", "TSK_DataModel.jar")
try:
open(chk)
except IOError as e:
if(not tryredo):
errorem += "DataModel Java build failed.\n"
attachl.append(antpth)
Emailer.send_email(parsed, errorem, attachl, False)
passed = False
tryredo = True
elif (succd != 0 and (not tryredo)):
errorem += "Autopsy build failed.\n"
attachl.append(antpth)
Emailer.send_email(parsed, errorem, attachl, False)
tryredo = True
elif (succd != 0):
passed = False
def main():
errore = ""
attachli = []
config_file = ""
arg = sys.argv.pop(0)
arg = sys.argv.pop(0)
config_file = arg
parsedin = parse(config_file)
compile(errore, attachli, parsedin)
class OS:
LINUX, MAC, WIN, CYGWIN = range(4)
if __name__ == "__main__":
global SYS
if _platform == "linux" or _platform == "linux2":
SYS = OS.LINUX
elif _platform == "darwin":
SYS = OS.MAC
elif _platform == "win32":
SYS = OS.WIN
elif _platform == "cygwin":
SYS = OS.CYGWIN
if SYS is OS.WIN or SYS is OS.CYGWIN:
main()
else:
print("We only support Windows and Cygwin at this time.")
import codecs
import datetime
import logging
import os
import re
import shutil
import socket
import sqlite3
import subprocess
import sys
from sys import platform as _platform
import time
import traceback
import xml
from xml.dom.minidom import parse, parseString
import Emailer
from regression_utils import *
def compile(errore, attachli, parsedin):
global redo
global tryredo
global failedbool
global errorem
errorem = errore
global attachl
attachl = attachli
global passed
global parsed
parsed = parsedin
passed = True
tryredo = False
redo = True
while(redo):
passed = True
if(passed):
gitPull("sleuthkit")
if(passed):
vsBuild()
if(passed):
gitPull("autopsy")
if(passed):
antBuild("datamodel", False)
if(passed):
antBuild("autopsy", True)
if(passed):
redo = False
else:
print("Compile Failed")
time.sleep(3600)
attachl = []
errorem = "The test standard didn't match the gold standard.\n"
failedbool = False
if(tryredo):
errorem = ""
errorem += "Rebuilt properly.\n"
Emailer.send_email(parsed, errorem, attachl, True)
attachl = []
passed = True
#Pulls from git
def gitPull(TskOrAutopsy):
global SYS
global errorem
global attachl
ccwd = ""
gppth = make_local_path("..", "GitPullOutput" + TskOrAutopsy + ".txt")
attachl.append(gppth)
gpout = open(gppth, 'a')
toPull = "https://www.github.com/sleuthkit/" + TskOrAutopsy
call = ["git", "pull", toPull]
if TskOrAutopsy == "sleuthkit":
ccwd = os.path.join("..", "..", "..", "sleuthkit")
else:
ccwd = os.path.join("..", "..")
subprocess.call(call, stdout=sys.stdout, cwd=ccwd)
gpout.close()
#Builds TSK as a win32 applicatiion
def vsBuild():
global redo
global tryredo
global passed
global parsed
#Please ensure that the current working directory is $autopsy/testing/script
oldpath = os.getcwd()
os.chdir(os.path.join("..", "..", "..","sleuthkit", "win32"))
vs = []
vs.append("/cygdrive/c/windows/microsoft.NET/framework/v4.0.30319/MSBuild.exe")
vs.append(os.path.join("Tsk-win.sln"))
vs.append("/p:configuration=release")
vs.append("/p:platform=win32")
vs.append("/t:clean")
vs.append("/t:rebuild")
print(vs)
VSpth = make_local_path("..", "VSOutput.txt")
VSout = open(VSpth, 'a')
subprocess.call(vs, stdout=VSout)
VSout.close()
os.chdir(oldpath)
chk = os.path.join("..", "..", "..","sleuthkit", "win32", "Release", "libtsk_jni.dll")
try:
open(chk)
except IOError as e:
global errorem
global attachl
if(not tryredo):
errorem += "LIBTSK C++ failed to build.\n"
attachl.append(VSpth)
send_email(parsed, errorem, attachl, False)
tryredo = True
passed = False
redo = True
#Builds Autopsy or the Datamodel
def antBuild(which, Build):
global redo
global passed
global tryredo
global parsed
directory = os.path.join("..", "..")
ant = []
if which == "datamodel":
directory = os.path.join("..", "..", "..", "sleuthkit", "bindings", "java")
ant.append("ant")
ant.append("-f")
ant.append(directory)
ant.append("clean")
if(Build):
ant.append("build")
else:
ant.append("dist")
antpth = make_local_path("..", "ant" + which + "Output.txt")
antout = open(antpth, 'a')
succd = subprocess.call(ant, stdout=antout)
antout.close()
global errorem
global attachl
if which == "datamodel":
chk = os.path.join("..", "..", "..","sleuthkit", "bindings", "java", "dist", "TSK_DataModel.jar")
try:
open(chk)
except IOError as e:
if(not tryredo):
errorem += "DataModel Java build failed.\n"
attachl.append(antpth)
Emailer.send_email(parsed, errorem, attachl, False)
passed = False
tryredo = True
elif (succd != 0 and (not tryredo)):
errorem += "Autopsy build failed.\n"
attachl.append(antpth)
Emailer.send_email(parsed, errorem, attachl, False)
tryredo = True
elif (succd != 0):
passed = False
def main():
errore = ""
attachli = []
config_file = ""
arg = sys.argv.pop(0)
arg = sys.argv.pop(0)
config_file = arg
parsedin = parse(config_file)
compile(errore, attachli, parsedin)
class OS:
LINUX, MAC, WIN, CYGWIN = range(4)
if __name__ == "__main__":
global SYS
if _platform == "linux" or _platform == "linux2":
SYS = OS.LINUX
elif _platform == "darwin":
SYS = OS.MAC
elif _platform == "win32":
SYS = OS.WIN
elif _platform == "cygwin":
SYS = OS.CYGWIN
if SYS is OS.WIN or SYS is OS.CYGWIN:
main()
else:
print("We only support Windows and Cygwin at this time.")

284
test/script/tskdbdiff.py Normal file
View File

@@ -0,0 +1,284 @@
import re
import sqlite3
import subprocess
import shutil
import os
import codecs
import datetime
import sys
class TskDbDiff(object):
"""Represents the differences between the gold and output databases.
Contains methods to compare two databases.
Attributes:
gold_artifacts:
autopsy_artifacts:
gold_attributes:
autopsy_attributes:
gold_objects:
autopsy_objects:
artifact_comparison:
attribute_comparision:
report_errors: a listof_listof_String, the error messages that will be
printed to screen in the run_diff method
passed: a boolean, did the diff pass?
autopsy_db_file:
gold_db_file:
"""
def __init__(self, output_db, gold_db, output_dir=None, gold_bb_dump=None, gold_dump=None, verbose=False):
"""Constructor for TskDbDiff.
Args:
output_db_path: a pathto_File, the output database
gold_db_path: a pathto_File, the gold database
output_dir: (optional) a pathto_Dir, the location where the generated files will be put.
gold_bb_dump: (optional) a pathto_File, the location where the gold blackboard dump is located
gold_dump: (optional) a pathto_File, the location where the gold non-blackboard dump is located
verbose: (optional) a boolean, should the diff results be printed to stdout?
"""
self.output_db_file = output_db
self.gold_db_file = gold_db
self.output_dir = output_dir
self.gold_bb_dump = gold_bb_dump
self.gold_dump = gold_dump
self._generate_gold_dump = gold_dump is None
self._generate_gold_bb_dump = gold_bb_dump is None
self._bb_dump_diff = ""
self._dump_diff = ""
self._bb_dump = ""
self._dump = ""
self.verbose = verbose
def run_diff(self):
"""Compare the databases.
Raises:
TskDbDiffException: if an error occurs while diffing or dumping the database
"""
self._init_diff()
# generate the gold database dumps if necessary
if self._generate_gold_dump:
TskDbDiff._dump_output_db_nonbb(self.gold_db_file, self.gold_dump)
if self._generate_gold_bb_dump:
TskDbDiff._dump_output_db_bb(self.gold_db_file, self.gold_bb_dump)
# generate the output database dumps
TskDbDiff.dump_output_db(self.output_db_file, self._dump, self._bb_dump)
dump_diff_pass = self._diff(self._dump, self.gold_dump, self._dump_diff)
bb_dump_diff_pass = self._diff(self._bb_dump, self.gold_bb_dump, self._bb_dump_diff)
self._cleanup_diff()
return dump_diff_pass, bb_dump_diff_pass
def _init_diff(self):
"""Set up the necessary files based on the arguments given at construction"""
if self.output_dir is None:
# No stored files
self._bb_dump = TskDbDiff._get_tmp_file("SortedData", ".txt")
self._bb_dump_diff = TskDbDiff._get_tmp_file("SortedData-Diff", ".txt")
self._dump = TskDbDiff._get_tmp_file("DBDump", ".txt")
self._dump_diff = TskDbDiff._get_tmp_file("DBDump-Diff", ".txt")
else:
self._bb_dump = os.path.join(self.output_dir, "SortedData.txt")
self._bb_dump_diff = os.path.join(self.output_dir, "SortedData-Diff.txt")
self._dump = os.path.join(self.output_dir, "DBDump.txt")
self._dump_diff = os.path.join(self.output_dir, "DBDump-Diff.txt")
if self.gold_bb_dump is None:
self.gold_bb_dump = TskDbDiff._get_tmp_file("GoldSortedData", ".txt")
self.gold_dump = TskDbDiff._get_tmp_file("GoldDBDump", ".txt")
def _cleanup_diff(self):
if self.output_dir is None:
#cleanup temp files
os.remove(self._dump)
os.remove(self._bb_dump)
if os.path.isfile(self._dump_diff):
os.remove(self._dump_diff)
if os.path.isfile(self._bb_dump_diff):
os.remove(self._bb_dump_diff)
if self.gold_bb_dump is None:
os.remove(self.gold_bb_dump)
os.remove(self.gold_dump)
def _diff(self, output_file, gold_file, diff_path):
"""Compare two text files.
Args:
output_file: a pathto_File, the output text file
gold_file: a pathto_File, the input text file
"""
if(not os.path.isfile(output_file)):
return False
output_data = codecs.open(output_file, "r", "utf_8").read()
gold_data = codecs.open(gold_file, "r", "utf_8").read()
if (not(gold_data == output_data)):
diff_file = codecs.open(diff_path, "wb", "utf_8")
dffcmdlst = ["diff", output_file, gold_file]
subprocess.call(dffcmdlst, stdout = diff_file)
return False
else:
return True
def _dump_output_db_bb(db_file, bb_dump_file):
"""Dumps sorted text results to the given output location.
Smart method that deals with a blackboard comparison to avoid issues
with different IDs based on when artifacts were created.
Args:
db_file: a pathto_File, the output database.
bb_dump_file: a pathto_File, the sorted dump file to write to
"""
unsorted_dump = TskDbDiff._get_tmp_file("dump_data", ".txt")
conn = sqlite3.connect(db_file)
autopsy_cur2 = conn.cursor()
# Get the list of all artifacts
# @@@ Could add a SORT by parent_path in here since that is how we are going to later sort it.
autopsy_cur2.execute("SELECT tsk_files.parent_path, tsk_files.name, blackboard_artifact_types.display_name, blackboard_artifacts.artifact_id FROM blackboard_artifact_types INNER JOIN blackboard_artifacts ON blackboard_artifact_types.artifact_type_id = blackboard_artifacts.artifact_type_id INNER JOIN tsk_files ON tsk_files.obj_id = blackboard_artifacts.obj_id")
database_log = codecs.open(unsorted_dump, "wb", "utf_8")
rw = autopsy_cur2.fetchone()
appnd = False
counter = 0
artifact_count = 0
artifact_fail = 0
# Cycle through artifacts
try:
while (rw != None):
# File Name and artifact type
if(rw[0] != None):
database_log.write(rw[0] + rw[1] + ' <artifact type="' + rw[2] + '" > ')
else:
database_log.write(rw[1] + ' <artifact type="' + rw[2] + '" > ')
# Get attributes for this artifact
autopsy_cur1 = conn.cursor()
looptry = True
artifact_count += 1
try:
key = ""
key = str(rw[3])
key = key,
autopsy_cur1.execute("SELECT blackboard_attributes.source, blackboard_attribute_types.display_name, blackboard_attributes.value_type, blackboard_attributes.value_text, blackboard_attributes.value_int32, blackboard_attributes.value_int64, blackboard_attributes.value_double FROM blackboard_attributes INNER JOIN blackboard_attribute_types ON blackboard_attributes.attribute_type_id = blackboard_attribute_types.attribute_type_id WHERE artifact_id =? ORDER BY blackboard_attributes.source, blackboard_attribute_types.display_name, blackboard_attributes.value_type, blackboard_attributes.value_text, blackboard_attributes.value_int32, blackboard_attributes.value_int64, blackboard_attributes.value_double", key)
attributes = autopsy_cur1.fetchall()
except sqlite3.Error as e:
msg ="Attributes in artifact id (in output DB)# " + str(rw[3]) + " encountered an error: " + str(e) +" .\n"
looptry = False
artifact_fail += 1
database_log.write('Error Extracting Attributes')
database_log.close()
raise TskDbDiffException(msg)
# Print attributes
if(looptry == True):
src = attributes[0][0]
for attr in attributes:
val = 3 + attr[2]
numvals = 0
for x in range(3, 6):
if(attr[x] != None):
numvals += 1
if(numvals > 1):
msg = "There were too many values for attribute type: " + attr[1] + " for artifact with id #" + str(rw[3]) + ".\n"
if(not attr[0] == src):
msg ="There were inconsistent sources for artifact with id #" + str(rw[3]) + ".\n"
try:
database_log.write('<attribute source="' + attr[0] + '" type="' + attr[1] + '" value="')
inpval = attr[val]
if((type(inpval) != 'unicode') or (type(inpval) != 'str')):
inpval = str(inpval)
patrn = re.compile("[\n\0\a\b\r\f\e]")
inpval = re.sub(patrn, ' ', inpval)
database_log.write(inpval)
except IOError as e:
raise TskDbDiffException("Unexpected IO error while writing to database log." + str(e))
database_log.write('" />')
database_log.write(' <artifact/>\n')
rw = autopsy_cur2.fetchone()
# Now sort the file
srtcmdlst = ["sort", unsorted_dump, "-o", bb_dump_file]
subprocess.call(srtcmdlst)
print(artifact_fail)
if(artifact_fail > 0):
msg ="There were " + str(artifact_count) + " artifacts and " + str(artifact_fail) + " threw an exception while loading.\n"
except Exception as e:
raise TskDbDiffException("Unexpected error while dumping blackboard database: " + str(e))
finally:
database_log.close()
def _dump_output_db_nonbb(db_file, dump_file):
"""Dumps a database to a text file.
Does not dump the artifact and attributes.
Args:
db_file: a pathto_File, the database file to dump
dump_file: a pathto_File, the location to dump the non-blackboard database items
"""
backup_db_file = TskDbDiff._get_tmp_file("tsk_backup_db", ".db")
shutil.copy(db_file, backup_db_file)
conn = sqlite3.connect(backup_db_file)
# Delete the blackboard tables
conn.execute("DROP TABLE blackboard_artifacts")
conn.execute("DROP TABLE blackboard_attributes")
# Write to the database dump
with codecs.open(dump_file, "wb", "utf_8") as db_log:
for line in conn.iterdump():
db_log.write('%s\n' % line)
# cleanup the backup
os.remove(backup_db_file)
def dump_output_db(db_file, dump_file, bb_dump_file):
"""Dumps the given database to text files for later comparison.
Args:
db_file: a pathto_File, the database file to dump
dump_file: a pathto_File, the location to dump the non-blackboard database items
bb_dump_file: a pathto_File, the location to dump the blackboard database items
"""
TskDbDiff._dump_output_db_nonbb(db_file, dump_file)
TskDbDiff._dump_output_db_bb(db_file, bb_dump_file)
def _get_tmp_file(base, ext):
time = datetime.datetime.now().time().strftime("%H%M%f")
return os.path.join(os.environ['TMP'], base + time + ext)
class TskDbDiffException(Exception):
pass
def main():
try:
sys.argv.pop(0)
output_db = sys.argv.pop(0)
gold_db = sys.argv.pop(0)
except:
print("usage: tskdbdiff [OUPUT DB PATH] [GOLD DB PATH]")
sys.exit()
db_diff = TskDbDiff(output_db, gold_db)
dump_passed, bb_dump_passed = db_diff.run_diff()
if dump_passed and bb_dump_passed:
print("Database comparison passed.")
elif not dump_passed:
print("Non blackboard database comparison failed.")
elif not bb_dump_passed:
print("Blackboard database comparison failed.")
return 0
if __name__ == "__main__":
sys.exit(main())

File diff suppressed because it is too large Load Diff