diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties
index 2a9cd7b456..aa2b4b9297 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties
@@ -6,3 +6,4 @@ DataContentViewerOtherCases.showCommonalityMenuItem.text=Show Frequency
DataContentViewerOtherCases.earliestCaseDate.text=Earliest Case Date
DataContentViewerOtherCases.earliestCaseLabel.toolTipText=
DataContentViewerOtherCases.earliestCaseLabel.text=Central Repository Starting Date:
+DataContentViewerOtherCases.foundInLabel.text=
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form
index 1f1e497aa7..53f6f6fc4b 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form
@@ -73,7 +73,7 @@
-
+
@@ -96,10 +96,10 @@
-
+
-
+
@@ -117,31 +117,28 @@
-
-
-
-
-
+
-
+
-
+
+
+
-
-
+
+
+
-
-
-
+
@@ -198,26 +195,13 @@
-
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java
index 6082dce81b..4762da103a 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java
@@ -1,7 +1,7 @@
/*
* Central Repository
*
- * Copyright 2015-2018 Basis Technology Corp.
+ * Copyright 2015-2019 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,10 +33,12 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.JFileChooser;
@@ -46,9 +48,13 @@ import static javax.swing.JOptionPane.DEFAULT_OPTION;
import static javax.swing.JOptionPane.PLAIN_MESSAGE;
import static javax.swing.JOptionPane.ERROR_MESSAGE;
import javax.swing.JPanel;
+import javax.swing.RowSorter;
+import javax.swing.SortOrder;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
+import javax.swing.table.TableModel;
+import javax.swing.table.TableRowSorter;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;
import org.openide.nodes.Node;
@@ -138,7 +144,20 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// Set background of every nth row as light grey.
TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer();
otherCasesTable.setDefaultRenderer(Object.class, renderer);
-
+
+ // Configure column sorting.
+ TableRowSorter sorter = new TableRowSorter<>(otherCasesTable.getModel());
+ otherCasesTable.setRowSorter(sorter);
+ List sortKeys = new ArrayList<>();
+
+ int caseNameColumnIndex = DataContentViewerOtherCasesTableModel.TableColumns.CASE_NAME.ordinal();
+ sortKeys.add(new RowSorter.SortKey(caseNameColumnIndex, SortOrder.ASCENDING));
+
+ int dataSourceColumnIndex = DataContentViewerOtherCasesTableModel.TableColumns.DATA_SOURCE.ordinal();
+ sortKeys.add(new RowSorter.SortKey(dataSourceColumnIndex, SortOrder.ASCENDING));
+
+ sorter.setSortKeys(sortKeys);
+ sorter.sort();
}
@Messages({"DataContentViewerOtherCases.correlatedArtifacts.isEmpty=There are no files or artifacts to correlate.",
@@ -306,6 +325,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
tableModel.clearTable();
correlationAttributes.clear();
earliestCaseDate.setText(Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable());
+ foundInLabel.setText("");
}
@Override
@@ -337,6 +357,40 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
public int isPreferred(Node node) {
return 1;
}
+
+ /**
+ * Set the number of unique cases and data sources.
+ */
+ @Messages({
+ "DataContentViewerOtherCases.foundIn.text=Found %d instances in %d cases and %d data sources."
+ })
+ private void setOccurrenceCounts() {
+ DataContentViewerOtherCasesTableModel model = (DataContentViewerOtherCasesTableModel) otherCasesTable.getModel();
+
+ int caseColumnIndex = DataContentViewerOtherCasesTableModel.TableColumns.CASE_NAME.ordinal();
+ int deviceColumnIndex = DataContentViewerOtherCasesTableModel.TableColumns.DEVICE.ordinal();
+
+ /*
+ * We need a unique set of data sources. We rely on device ID for this.
+ * To mitigate edge cases where a device ID could be duplicated in the
+ * same case (e.g. "report.xml"), we put the device ID and case name in
+ * a key-value pair.
+ *
+ * Note: Relying on the case name isn't a fool-proof way of determining
+ * a case to be unique. We should improve this in the future.
+ */
+ Set cases = new HashSet<>();
+ Map devices = new HashMap();
+
+ for (int i=0; i < model.getRowCount(); i++) {
+ String caseName = (String) model.getValueAt(i, caseColumnIndex);
+ String deviceId = (String) model.getValueAt(i, deviceColumnIndex);
+ cases.add(caseName);
+ devices.put(deviceId, caseName);
+ }
+
+ foundInLabel.setText(String.format(Bundle.DataContentViewerOtherCases_foundIn_text(), model.getRowCount(), cases.size(), devices.size()));
+ }
/**
* Get the associated BlackboardArtifact from a node, if it exists.
@@ -720,7 +774,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} else {
setColumnWidths();
}
+
setEarliestCaseDate();
+ setOccurrenceCounts();
}
/**
@@ -772,7 +828,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
otherCasesTable = new javax.swing.JTable();
earliestCaseLabel = new javax.swing.JLabel();
earliestCaseDate = new javax.swing.JLabel();
- tableStatusPanel = new javax.swing.JPanel();
+ foundInLabel = new javax.swing.JLabel();
rightClickPopupMenu.addPopupMenuListener(new javax.swing.event.PopupMenuListener() {
public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) {
@@ -818,44 +874,31 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
org.openide.awt.Mnemonics.setLocalizedText(earliestCaseDate, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.earliestCaseDate.text")); // NOI18N
- tableStatusPanel.setPreferredSize(new java.awt.Dimension(1500, 16));
-
- javax.swing.GroupLayout tableStatusPanelLayout = new javax.swing.GroupLayout(tableStatusPanel);
- tableStatusPanel.setLayout(tableStatusPanelLayout);
- tableStatusPanelLayout.setHorizontalGroup(
- tableStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGap(0, 0, Short.MAX_VALUE)
- );
- tableStatusPanelLayout.setVerticalGroup(
- tableStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGap(0, 16, Short.MAX_VALUE)
- );
+ org.openide.awt.Mnemonics.setLocalizedText(foundInLabel, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.foundInLabel.text")); // NOI18N
javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel);
tableContainerPanel.setLayout(tableContainerPanelLayout);
tableContainerPanelLayout.setHorizontalGroup(
tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup()
- .addComponent(tableStatusPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 1282, Short.MAX_VALUE)
- .addGap(218, 218, 218))
- .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1508, Short.MAX_VALUE)
.addGroup(tableContainerPanelLayout.createSequentialGroup()
.addComponent(earliestCaseLabel)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(earliestCaseDate)
- .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addGap(66, 66, 66)
+ .addComponent(foundInLabel)
+ .addGap(0, 1157, Short.MAX_VALUE))
);
tableContainerPanelLayout.setVerticalGroup(
tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup()
- .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE)
- .addGap(2, 2, 2)
+ .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 71, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(earliestCaseLabel)
- .addComponent(earliestCaseDate))
- .addGap(0, 0, 0)
- .addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addGap(0, 0, 0))
+ .addComponent(earliestCaseDate)
+ .addComponent(foundInLabel))
+ .addGap(6, 6, 6))
);
javax.swing.GroupLayout otherCasesPanelLayout = new javax.swing.GroupLayout(otherCasesPanel);
@@ -868,10 +911,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
);
otherCasesPanelLayout.setVerticalGroup(
otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGap(0, 483, Short.MAX_VALUE)
+ .addGap(0, 61, Short.MAX_VALUE)
.addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(otherCasesPanelLayout.createSequentialGroup()
- .addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE)
+ .addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 53, Short.MAX_VALUE)
.addGap(0, 0, 0)))
);
@@ -883,7 +926,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE)
+ .addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 53, Short.MAX_VALUE)
);
}// //GEN-END:initComponents
@@ -907,6 +950,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
private javax.swing.JLabel earliestCaseDate;
private javax.swing.JLabel earliestCaseLabel;
private javax.swing.JMenuItem exportToCSVMenuItem;
+ private javax.swing.JLabel foundInLabel;
private javax.swing.JPanel otherCasesPanel;
private javax.swing.JTable otherCasesTable;
private javax.swing.JPopupMenu rightClickPopupMenu;
@@ -915,7 +959,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
private javax.swing.JMenuItem showCommonalityMenuItem;
private javax.swing.JPanel tableContainerPanel;
private javax.swing.JScrollPane tableScrollPane;
- private javax.swing.JPanel tableStatusPanel;
// End of variables declaration//GEN-END:variables
/**
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties
index b46e2cf44a..e10eb1b67c 100644
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties
@@ -83,3 +83,4 @@ MediaViewImagePanel.zoomResetButton.text=Reset
MediaViewImagePanel.zoomTextField.text=
MediaViewImagePanel.rotationTextField.text=
MediaViewImagePanel.rotateLeftButton.toolTipText=
+HtmlPanel.showImagesToggleButton.text=Show Images
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.form
index d07831cafe..b3a7244a10 100644
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.form
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.form
@@ -11,6 +11,7 @@
+
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java
index 84f216e253..ab92f3f543 100644
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2018 Basis Technology Corp.
+ * Copyright 2018-2019 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -49,7 +49,8 @@ public class FileViewer extends javax.swing.JPanel implements DataContentViewer
private final FileTypeViewer[] KNOWN_VIEWERS = new FileTypeViewer[]{
new SQLiteViewer(),
new PListViewer(),
- new MediaFileViewer()
+ new MediaFileViewer(),
+ new HtmlViewer()
};
private FileTypeViewer lastViewer;
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form
new file mode 100755
index 0000000000..54e6d45006
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.form
@@ -0,0 +1,65 @@
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java
new file mode 100755
index 0000000000..1699536c94
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlPanel.java
@@ -0,0 +1,165 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2019 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.contentviewers;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.openide.util.NbBundle.Messages;
+
+/**
+ * A file content viewer for HTML files.
+ */
+@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
+final class HtmlPanel extends javax.swing.JPanel {
+
+ private static final long serialVersionUID = 1L;
+
+ private String htmlText;
+
+ /**
+ * Creates new form HtmlViewerPanel
+ */
+ HtmlPanel() {
+ initComponents();
+
+ Utilities.configureTextPaneAsHtml(htmlbodyTextPane);
+ }
+
+ /**
+ * Set the text pane's HTML text and refresh the view to display it.
+ *
+ * @param htmlText The HTML text to be applied to the text pane.
+ */
+ void setHtmlText(String htmlText) {
+ this.htmlText = htmlText;
+ refresh();
+ }
+
+ /**
+ * Clear the HTML in the text pane and disable the show/hide button.
+ */
+ void reset() {
+ htmlbodyTextPane.setText("");
+ showImagesToggleButton.setEnabled(false);
+ }
+
+ /**
+ * Guarantee the HTML text has 'html' and 'body' tags.
+ *
+ * @param htmlText The HTML text
+ *
+ * @return The HTML text with the 'html' and 'body' tags applied.
+ */
+ private String wrapInHtmlBody(String htmlText) {
+ return "" + htmlText + "";
+ }
+
+ /**
+ * Cleans out input HTML string
+ *
+ * @param htmlInString The HTML string to cleanse
+ *
+ * @return The cleansed HTML String
+ */
+ private String cleanseHTML(String htmlInString) {
+
+ Document doc = Jsoup.parse(htmlInString);
+
+ // Update all 'img' tags.
+ doc.select("img[src]").forEach(img -> img.attr("src", ""));
+
+ return doc.html();
+ }
+
+ /**
+ * Refresh the panel to reflect the current show/hide images setting.
+ */
+ @Messages({
+ "HtmlPanel_showImagesToggleButton_show=Show Images",
+ "HtmlPanel_showImagesToggleButton_hide=Hide Images"
+ })
+ private void refresh() {
+ if (false == htmlText.isEmpty()) {
+ if (showImagesToggleButton.isSelected()) {
+ showImagesToggleButton.setText(Bundle.HtmlPanel_showImagesToggleButton_hide());
+ this.htmlbodyTextPane.setText(wrapInHtmlBody(htmlText));
+ } else {
+ showImagesToggleButton.setText(Bundle.HtmlPanel_showImagesToggleButton_show());
+ this.htmlbodyTextPane.setText(wrapInHtmlBody(cleanseHTML(htmlText)));
+ }
+
+ htmlbodyTextPane.setCaretPosition(0);
+ showImagesToggleButton.setEnabled(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")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ htmlScrollPane = new javax.swing.JScrollPane();
+ htmlbodyTextPane = new javax.swing.JTextPane();
+ showImagesToggleButton = new javax.swing.JToggleButton();
+
+ htmlScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
+
+ htmlbodyTextPane.setEditable(false);
+ htmlScrollPane.setViewportView(htmlbodyTextPane);
+
+ org.openide.awt.Mnemonics.setLocalizedText(showImagesToggleButton, org.openide.util.NbBundle.getMessage(HtmlPanel.class, "HtmlPanel.showImagesToggleButton.text")); // NOI18N
+ showImagesToggleButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ showImagesToggleButtonActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(htmlScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(showImagesToggleButton)
+ .addGap(0, 0, Short.MAX_VALUE))
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(showImagesToggleButton)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(htmlScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 71, Short.MAX_VALUE))
+ );
+ }// //GEN-END:initComponents
+
+ private void showImagesToggleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showImagesToggleButtonActionPerformed
+ refresh();
+ }//GEN-LAST:event_showImagesToggleButtonActionPerformed
+
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JScrollPane htmlScrollPane;
+ private javax.swing.JTextPane htmlbodyTextPane;
+ private javax.swing.JToggleButton showImagesToggleButton;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.form
new file mode 100755
index 0000000000..a08d9e9b31
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.form
@@ -0,0 +1,40 @@
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.java
new file mode 100755
index 0000000000..ff22d952e7
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.java
@@ -0,0 +1,128 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2019 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.contentviewers;
+
+import java.awt.Component;
+import java.awt.Cursor;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Level;
+import org.openide.windows.WindowManager;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.datamodel.AbstractFile;
+import org.sleuthkit.datamodel.TskCoreException;
+
+/**
+ * A file content viewer for HTML files.
+ */
+@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
+final class HtmlViewer extends javax.swing.JPanel implements FileTypeViewer {
+
+ private static final long serialVersionUID = 1L;
+ private static final Logger logger = Logger.getLogger(HtmlViewer.class.getName());
+
+ private static final String[] SUPPORTED_MIMETYPES = new String[]{
+ "text/html",
+ "application/xhtml+xml"
+ };
+
+ /**
+ * Creates new form HtmlViewerPanel
+ */
+ HtmlViewer() {
+ initComponents();
+ }
+
+ /**
+ * Retrieve the HTML text content from the supplied file.
+ *
+ * @param abstractFile The file to read.
+ *
+ * @return The text content of the file.
+ */
+ private String getHtmlText(AbstractFile abstractFile) {
+ try {
+ int fileSize = (int) abstractFile.getSize();
+ byte[] buffer = new byte[fileSize];
+ abstractFile.read(buffer, 0, fileSize);
+ return new String(buffer);
+ } catch (TskCoreException ex) {
+ logger.log(Level.SEVERE, String.format("Unable to read from file '%s' (id=%d).",
+ abstractFile.getName(), abstractFile.getId()), ex);
+ }
+
+ return null;
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ htmlPanel = new org.sleuthkit.autopsy.contentviewers.HtmlPanel();
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(htmlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(htmlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addContainerGap())
+ );
+ }// //GEN-END:initComponents
+
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private org.sleuthkit.autopsy.contentviewers.HtmlPanel htmlPanel;
+ // End of variables declaration//GEN-END:variables
+
+ @Override
+ public List getSupportedMIMETypes() {
+ return Arrays.asList(SUPPORTED_MIMETYPES);
+ }
+
+ @Override
+ public void setFile(AbstractFile file) {
+ WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ htmlPanel.setHtmlText(getHtmlText(file));
+ WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }
+
+ @Override
+ public Component getComponent() {
+ return this;
+ }
+
+ @Override
+ public void resetComponent() {
+ htmlPanel.reset();
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java
index e501b136d5..b98c350d68 100644
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java
@@ -126,7 +126,6 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
scene.getStylesheets().add(MediaViewImagePanel.class.getResource("MediaViewImagePanel.css").toExternalForm()); //NOI18N
fxPanel.setScene(scene);
- //bind size of image to that of scene, while keeping proportions
fxImageView.setSmooth(true);
fxImageView.setCache(true);
@@ -142,11 +141,13 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
}
/**
- * clear the displayed image
+ * Clear the displayed image
*/
public void reset() {
Platform.runLater(() -> {
+ fxImageView.setViewport(new Rectangle2D(0, 0, 0, 0));
fxImageView.setImage(null);
+
scrollPane.setContent(null);
scrollPane.setContent(fxImageView);
});
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.form
index c1400964a9..1172483699 100644
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.form
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.form
@@ -274,50 +274,17 @@
-
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java
index ef631eeb02..00c87b3417 100644
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2017-2018 Basis Technology Corp.
+ * Copyright 2017-2019 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -106,9 +106,12 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
attachmentsScrollPane.setViewportView(drp);
msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, true);
- textAreas = Arrays.asList(headersTextArea, textbodyTextArea, htmlbodyTextPane, rtfbodyTextPane);
+ /*
+ * HTML tab uses the HtmlPanel instead of an internal text pane, so we
+ * use 'null' for that index.
+ */
+ textAreas = Arrays.asList(headersTextArea, textbodyTextArea, null, rtfbodyTextPane);
- Utilities.configureTextPaneAsHtml(htmlbodyTextPane);
Utilities.configureTextPaneAsRtf(rtfbodyTextPane);
resetComponent();
@@ -150,9 +153,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
textbodyScrollPane = new javax.swing.JScrollPane();
textbodyTextArea = new javax.swing.JTextArea();
htmlPane = new javax.swing.JPanel();
- htmlScrollPane = new javax.swing.JScrollPane();
- htmlbodyTextPane = new javax.swing.JTextPane();
- showImagesToggleButton = new javax.swing.JToggleButton();
+ htmlPanel = new org.sleuthkit.autopsy.contentviewers.HtmlPanel();
rtfbodyScrollPane = new javax.swing.JScrollPane();
rtfbodyTextPane = new javax.swing.JTextPane();
attachmentsPanel = new javax.swing.JPanel();
@@ -265,35 +266,15 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.textbodyScrollPane.TabConstraints.tabTitle"), textbodyScrollPane); // NOI18N
- htmlScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
-
- htmlbodyTextPane.setEditable(false);
- htmlScrollPane.setViewportView(htmlbodyTextPane);
-
- org.openide.awt.Mnemonics.setLocalizedText(showImagesToggleButton, "Show Images");
- showImagesToggleButton.addActionListener(new java.awt.event.ActionListener() {
- public void actionPerformed(java.awt.event.ActionEvent evt) {
- showImagesToggleButtonActionPerformed(evt);
- }
- });
-
javax.swing.GroupLayout htmlPaneLayout = new javax.swing.GroupLayout(htmlPane);
htmlPane.setLayout(htmlPaneLayout);
htmlPaneLayout.setHorizontalGroup(
htmlPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(htmlScrollPane)
- .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, htmlPaneLayout.createSequentialGroup()
- .addContainerGap(533, Short.MAX_VALUE)
- .addComponent(showImagesToggleButton)
- .addGap(3, 3, 3))
+ .addComponent(htmlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 647, Short.MAX_VALUE)
);
htmlPaneLayout.setVerticalGroup(
htmlPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(htmlPaneLayout.createSequentialGroup()
- .addComponent(showImagesToggleButton)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(htmlScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 333, Short.MAX_VALUE)
- .addGap(0, 0, 0))
+ .addComponent(htmlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 362, Short.MAX_VALUE)
);
msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.htmlPane.TabConstraints.tabTitle"), htmlPane); // NOI18N
@@ -358,26 +339,6 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
);
}// //GEN-END:initComponents
- @NbBundle.Messages({
- "MessageContentViewer.showImagesToggleButton.hide.text=Hide Images",
- "MessageContentViewer.showImagesToggleButton.text=Show Images"})
- private void showImagesToggleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showImagesToggleButtonActionPerformed
- try {
- String htmlText = getAttributeValueSafe(artifact, TSK_EMAIL_CONTENT_HTML);
- if (false == htmlText.isEmpty()) {
- if (showImagesToggleButton.isSelected()) {
- showImagesToggleButton.setText(Bundle.MessageContentViewer_showImagesToggleButton_hide_text());
- this.htmlbodyTextPane.setText(wrapInHtmlBody(htmlText));
- } else {
- showImagesToggleButton.setText(Bundle.MessageContentViewer_showImagesToggleButton_text());
- this.htmlbodyTextPane.setText(wrapInHtmlBody(cleanseHTML(htmlText)));
- }
- }
- } catch (TskCoreException ex) {
- LOGGER.log(Level.WARNING, "Failed to get attributes for email message.", ex); //NON-NLS
- }
- }//GEN-LAST:event_showImagesToggleButtonActionPerformed
-
private void viewInNewWindowButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewInNewWindowButtonActionPerformed
new NewWindowViewAction("View in new window", drpExplorerManager.getSelectedNodes()[0]).actionPerformed(evt);
}//GEN-LAST:event_viewInNewWindowButtonActionPerformed
@@ -396,12 +357,10 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
private javax.swing.JScrollPane headersScrollPane;
private javax.swing.JTextArea headersTextArea;
private javax.swing.JPanel htmlPane;
- private javax.swing.JScrollPane htmlScrollPane;
- private javax.swing.JTextPane htmlbodyTextPane;
+ private org.sleuthkit.autopsy.contentviewers.HtmlPanel htmlPanel;
private javax.swing.JTabbedPane msgbodyTabbedPane;
private javax.swing.JScrollPane rtfbodyScrollPane;
private javax.swing.JTextPane rtfbodyTextPane;
- private javax.swing.JToggleButton showImagesToggleButton;
private javax.swing.JLabel subjectLabel;
private javax.swing.JLabel subjectText;
private javax.swing.JScrollPane textbodyScrollPane;
@@ -505,9 +464,8 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
headersTextArea.setText("");
rtfbodyTextPane.setText("");
- htmlbodyTextPane.setText("");
+ htmlPanel.reset();
textbodyTextArea.setText("");
- showImagesToggleButton.setEnabled(false);
msgbodyTabbedPane.setEnabled(false);
}
@@ -567,12 +525,15 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
String attributeText = getAttributeValueSafe(artifact, type);
if (index == HTML_TAB_INDEX && StringUtils.isNotBlank(attributeText)) {
- //special case for HTML, we need to 'cleanse' it
- attributeText = wrapInHtmlBody(cleanseHTML(attributeText));
+ htmlPanel.setHtmlText(attributeText);
+ } else {
+ JTextComponent textComponent = textAreas.get(index);
+ if (textComponent != null) {
+ textComponent.setText(attributeText);
+ textComponent.setCaretPosition(0); //make sure we start at the top
+ }
}
- JTextComponent textComponent = textAreas.get(index);
- textComponent.setText(attributeText);
- textComponent.setCaretPosition(0); //make sure we start at the top
+
final boolean hasText = attributeText.length() > 0;
msgbodyTabbedPane.setEnabledAt(index, hasText);
@@ -613,10 +574,6 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
directionText.setEnabled(false);
ccLabel.setEnabled(true);
- showImagesToggleButton.setEnabled(true);
- showImagesToggleButton.setText("Show Images");
- showImagesToggleButton.setSelected(false);
-
try {
this.fromText.setText(getAttributeValueSafe(artifact, TSK_EMAIL_FROM));
this.fromText.setToolTipText(getAttributeValueSafe(artifact, TSK_EMAIL_FROM));
diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java
index 16c2b0e3e8..d9c142563b 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java
@@ -302,8 +302,9 @@ class MSOfficeEmbeddedContentExtractor {
}
List listOfExtractedImages = new ArrayList<>();
byte[] data = null;
+ int pictureNumber = 0; //added to ensure uniqueness in cases where suggestFullFileName returns duplicates
for (Picture picture : listOfAllPictures) {
- String fileName = picture.suggestFullFileName();
+ String fileName = UNKNOWN_IMAGE_NAME_PREFIX +pictureNumber +"."+ picture.suggestFileExtension();
try {
data = picture.getContent();
} catch (Exception ex) {
@@ -312,6 +313,7 @@ class MSOfficeEmbeddedContentExtractor {
writeExtractedImage(Paths.get(outputFolderPath, fileName).toString(), data);
// TODO Extract more info from the Picture viz ctime, crtime, atime, mtime
listOfExtractedImages.add(new ExtractedFile(fileName, getFileRelativePath(fileName), picture.getSize()));
+ pictureNumber++;
}
return listOfExtractedImages;
diff --git a/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java b/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java
index cb751fa53b..98805e420e 100644
--- a/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java
+++ b/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java
@@ -38,6 +38,9 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.datamodel.utils.FileTypeUtils.FileTypeCategory;
import org.sleuthkit.datamodel.AbstractFile;
+import org.sleuthkit.datamodel.BlackboardArtifact;
+import org.sleuthkit.datamodel.BlackboardArtifactTag;
+import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.FileSystem;
@@ -47,6 +50,7 @@ import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException;
+import org.sleuthkit.datamodel.TskDataException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.Volume;
import org.sleuthkit.datamodel.VolumeSystem;
@@ -66,7 +70,7 @@ public class CreatePortableCaseModule implements GeneralReportModule {
FileTypeCategory.EXECUTABLE, FileTypeCategory.IMAGE, FileTypeCategory.VIDEO);
private Case currentCase = null;
- private SleuthkitCase skCase = null;
+ private SleuthkitCase portableSkCase = null;
private File caseFolder = null;
private File copiedFilesFolder = null;
@@ -78,6 +82,15 @@ public class CreatePortableCaseModule implements GeneralReportModule {
// Maps old TagName to new TagName
private final Map oldTagNameToNewTagName = new HashMap<>();
+
+ // Map of old artifact type ID to new artifact type ID. There will only be changes if custom artifact types are present.
+ private final Map oldArtTypeIdToNewArtTypeId = new HashMap<>();
+
+ // Map of old attribute type ID to new attribute type ID. There will only be changes if custom attr types are present.
+ private final Map oldAttrTypeIdToNewAttrType = new HashMap<>();
+
+ // Map of old artifact ID to new artifact
+ private final Map oldArtifactIdToNewArtifact = new HashMap<>();
public CreatePortableCaseModule() {
// Nothing to do here
@@ -132,6 +145,8 @@ public class CreatePortableCaseModule implements GeneralReportModule {
"CreatePortableCaseModule.generateReport.copyingTags=Copying tags...",
"# {0} - tag name",
"CreatePortableCaseModule.generateReport.copyingFiles=Copying files tagged as {0}...",
+ "# {0} - tag name",
+ "CreatePortableCaseModule.generateReport.copyingArtifacts=Copying artifacts tagged as {0}...",
"# {0} - output folder",
"CreatePortableCaseModule.generateReport.outputDirDoesNotExist=Output folder {0} does not exist",
"# {0} - output folder",
@@ -139,7 +154,10 @@ public class CreatePortableCaseModule implements GeneralReportModule {
"CreatePortableCaseModule.generateReport.noTagsSelected=No tags selected for export.",
"CreatePortableCaseModule.generateReport.caseClosed=Current case has been closed",
"CreatePortableCaseModule.generateReport.errorCopyingTags=Error copying tags",
- "CreatePortableCaseModule.generateReport.errorCopyingFiles=Error copying tagged files"
+ "CreatePortableCaseModule.generateReport.errorCopyingFiles=Error copying tagged files",
+ "CreatePortableCaseModule.generateReport.errorCopyingArtifacts=Error copying tagged artifacts",
+ "# {0} - attribute type name",
+ "CreatePortableCaseModule.generateReport.errorLookingUpAttrType=Error looking up attribute type {0}",
})
@Override
public void generateReport(String reportPath, ReportProgressPanel progressPanel) {
@@ -182,10 +200,10 @@ public class CreatePortableCaseModule implements GeneralReportModule {
// Create the case.
- // skCase and caseFolder will be set here.
+ // portableSkCase and caseFolder will be set here.
progressPanel.updateStatusLabel(Bundle.CreatePortableCaseModule_generateReport_creatingCase());
createCase(outputDir, progressPanel);
- if (skCase == null) {
+ if (portableSkCase == null) {
// The error has already been handled
return;
}
@@ -200,7 +218,7 @@ public class CreatePortableCaseModule implements GeneralReportModule {
progressPanel.updateStatusLabel(Bundle.CreatePortableCaseModule_generateReport_copyingTags());
try {
for(TagName tagName:tagNames) {
- TagName newTagName = skCase.addOrUpdateTagName(tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus());
+ TagName newTagName = portableSkCase.addOrUpdateTagName(tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus());
oldTagNameToNewTagName.put(tagName, newTagName);
}
} catch (TskCoreException ex) {
@@ -222,6 +240,35 @@ public class CreatePortableCaseModule implements GeneralReportModule {
handleError("Error copying tagged files", Bundle.CreatePortableCaseModule_generateReport_errorCopyingFiles(), ex, progressPanel);
return;
}
+
+ // Set up tracking to support any custom artifact or attribute types
+ for (BlackboardArtifact.ARTIFACT_TYPE type:BlackboardArtifact.ARTIFACT_TYPE.values()) {
+ oldArtTypeIdToNewArtTypeId.put(type.getTypeID(), type.getTypeID());
+ }
+ for (BlackboardAttribute.ATTRIBUTE_TYPE type:BlackboardAttribute.ATTRIBUTE_TYPE.values()) {
+ try {
+ oldAttrTypeIdToNewAttrType.put(type.getTypeID(), portableSkCase.getAttributeType(type.getLabel()));
+ } catch (TskCoreException ex) {
+ handleError("Error looking up attribute name " + type.getLabel(),
+ Bundle.CreatePortableCaseModule_generateReport_errorLookingUpAttrType(type.getLabel()),
+ ex, progressPanel);
+ }
+ }
+
+ // Copy the tagged artifacts and associated files
+ try {
+ for(TagName tagName:tagNames) {
+ // Check for cancellation
+ if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
+ return;
+ }
+ progressPanel.updateStatusLabel(Bundle.CreatePortableCaseModule_generateReport_copyingArtifacts(tagName.getDisplayName()));
+ addArtifactsToPortableCase(tagName, progressPanel);
+ }
+ } catch (TskCoreException ex) {
+ handleError("Error copying tagged artifacts", Bundle.CreatePortableCaseModule_generateReport_errorCopyingArtifacts(), ex, progressPanel);
+ return;
+ }
// Close the case connections and clear out the maps
cleanup();
@@ -232,7 +279,7 @@ public class CreatePortableCaseModule implements GeneralReportModule {
/**
* Create the case directory and case database.
- * skCase will be set if this completes without error.
+ * portableSkCase will be set if this completes without error.
*
* @param outputDir The parent for the case folder
* @param progressPanel
@@ -258,7 +305,7 @@ public class CreatePortableCaseModule implements GeneralReportModule {
// Create the case
try {
- skCase = currentCase.createPortableCase(caseName, caseFolder);
+ portableSkCase = currentCase.createPortableCase(caseName, caseFolder);
} catch (TskCoreException ex) {
handleError("Error creating case " + caseName + " in folder " + caseFolder.toString(),
Bundle.CreatePortableCaseModule_createCase_errorCreatingCase(), ex, progressPanel);
@@ -294,14 +341,11 @@ public class CreatePortableCaseModule implements GeneralReportModule {
/**
* Add all files with a given tag to the portable case.
*
- * @param oldTagName
- * @param progressPanel
+ * @param oldTagName The TagName object from the current case
+ * @param progressPanel The progress panel
+ *
* @throws TskCoreException
*/
- @NbBundle.Messages({
- "# {0} - File name",
- "CreatePortableCaseModule.addFilesToPortableCase.copyingFile=Copying file {0}",
- })
private void addFilesToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel) throws TskCoreException {
// Get all the tags in the current case
@@ -317,29 +361,201 @@ public class CreatePortableCaseModule implements GeneralReportModule {
Content content = tag.getContent();
if (content instanceof AbstractFile) {
- AbstractFile file = (AbstractFile) content;
- String filePath = file.getParentPath() + file.getName();
- progressPanel.updateStatusLabel(Bundle.CreatePortableCaseModule_addFilesToPortableCase_copyingFile(filePath));
-
- long newFileId;
- CaseDbTransaction trans = skCase.beginTransaction();
- try {
- newFileId = copyContent(file, trans);
- trans.commit();
- } catch (TskCoreException ex) {
- trans.rollback();
- throw(ex);
- }
+ long newFileId = copyContentToPortableCase(content, progressPanel);
// Tag the file
if (! oldTagNameToNewTagName.containsKey(tag.getName())) {
throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName());
}
- skCase.addContentTag(newIdToContent.get(newFileId), oldTagNameToNewTagName.get(tag.getName()), tag.getComment(), tag.getBeginByteOffset(), tag.getEndByteOffset());
+ portableSkCase.addContentTag(newIdToContent.get(newFileId), oldTagNameToNewTagName.get(tag.getName()), tag.getComment(), tag.getBeginByteOffset(), tag.getEndByteOffset());
}
}
}
+ /**
+ * Add all artifacts with a given tag to the portable case.
+ *
+ * @param oldTagName The TagName object from the current case
+ * @param progressPanel The progress panel
+ *
+ * @throws TskCoreException
+ */
+ private void addArtifactsToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel) throws TskCoreException {
+
+ List tags = currentCase.getServices().getTagsManager().getBlackboardArtifactTagsByTagName(oldTagName);
+
+ // Copy the artifacts into the portable case along with their content and tag
+ for (BlackboardArtifactTag tag : tags) {
+
+ // Check for cancellation
+ if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
+ return;
+ }
+
+ // Copy the source content
+ Content content = tag.getContent();
+ long newContentId = copyContentToPortableCase(content, progressPanel);
+
+ // Copy the artifact
+ BlackboardArtifact newArtifact = copyArtifact(newContentId, tag.getArtifact());
+
+ // Tag the artfiact
+ if (! oldTagNameToNewTagName.containsKey(tag.getName())) {
+ throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName());
+ }
+ portableSkCase.addBlackboardArtifactTag(newArtifact, oldTagNameToNewTagName.get(tag.getName()), tag.getComment());
+ }
+ }
+
+ /**
+ * Copy an artifact into the new case. Will also copy any associated artifacts
+ *
+ * @param newContentId The content ID (in the portable case) of the source content
+ * @param artifactToCopy The artifact to copy
+ *
+ * @return The new artifact in the portable case
+ *
+ * @throws TskCoreException
+ */
+ private BlackboardArtifact copyArtifact(long newContentId, BlackboardArtifact artifactToCopy) throws TskCoreException {
+
+ if (oldArtifactIdToNewArtifact.containsKey(artifactToCopy.getArtifactID())) {
+ return oldArtifactIdToNewArtifact.get(artifactToCopy.getArtifactID());
+ }
+
+ // First create the associated artifact (if present)
+ BlackboardAttribute oldAssociatedAttribute = artifactToCopy.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
+ List newAttrs = new ArrayList<>();
+ if (oldAssociatedAttribute != null) {
+ BlackboardArtifact oldAssociatedArtifact = currentCase.getSleuthkitCase().getBlackboardArtifact(oldAssociatedAttribute.getValueLong());
+ BlackboardArtifact newAssociatedArtifact = copyArtifact(newContentId, oldAssociatedArtifact);
+ newAttrs.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
+ String.join(",", oldAssociatedAttribute.getSources()), newAssociatedArtifact.getArtifactID()));
+ }
+
+ // Create the new artifact
+ int newArtifactTypeId = getNewArtifactTypeId(artifactToCopy);
+ BlackboardArtifact newArtifact = portableSkCase.newBlackboardArtifact(newArtifactTypeId, newContentId);
+ List oldAttrs = artifactToCopy.getAttributes();
+
+ // Copy over each attribute, making sure the type is in the new case.
+ for (BlackboardAttribute oldAttr:oldAttrs) {
+
+ // The associated artifact has already been handled
+ if (oldAttr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
+ continue;
+ }
+
+ BlackboardAttribute.Type newAttributeType = getNewAttributeType(oldAttr);
+ switch (oldAttr.getValueType()) {
+ case BYTE:
+ newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
+ oldAttr.getValueBytes()));
+ break;
+ case DOUBLE:
+ newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
+ oldAttr.getValueDouble()));
+ break;
+ case INTEGER:
+ newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
+ oldAttr.getValueInt()));
+ break;
+ case DATETIME:
+ case LONG:
+ newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
+ oldAttr.getValueLong()));
+ break;
+ case STRING:
+ newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
+ oldAttr.getValueString()));
+ break;
+ default:
+ throw new TskCoreException("Unexpected attribute value type found: " + oldAttr.getValueType().getLabel());
+ }
+ }
+
+ newArtifact.addAttributes(newAttrs);
+
+ oldArtifactIdToNewArtifact.put(artifactToCopy.getArtifactID(), newArtifact);
+ return newArtifact;
+ }
+
+ /**
+ * Get the artifact type ID in the portable case and create new artifact type if needed.
+ * For built-in artifacts this will be the same as the original.
+ *
+ * @param oldArtifactTypeId The artifact type ID in the current case
+ *
+ * @return The corresponding artifact type ID in the portable case
+ */
+ private int getNewArtifactTypeId(BlackboardArtifact oldArtifact) throws TskCoreException {
+ if (oldArtTypeIdToNewArtTypeId.containsKey(oldArtifact.getArtifactTypeID())) {
+ return oldArtTypeIdToNewArtTypeId.get(oldArtifact.getArtifactTypeID());
+ }
+
+ BlackboardArtifact.Type oldCustomType = currentCase.getSleuthkitCase().getArtifactType(oldArtifact.getArtifactTypeName());
+ try {
+ BlackboardArtifact.Type newCustomType = portableSkCase.addBlackboardArtifactType(oldCustomType.getTypeName(), oldCustomType.getDisplayName());
+ oldArtTypeIdToNewArtTypeId.put(oldArtifact.getArtifactTypeID(), newCustomType.getTypeID());
+ return newCustomType.getTypeID();
+ } catch (TskDataException ex) {
+ throw new TskCoreException("Error creating new artifact type " + oldCustomType.getTypeName(), ex);
+ }
+ }
+
+ /**
+ * Get the attribute type ID in the portable case and create new attribute type if needed.
+ * For built-in attributes this will be the same as the original.
+ *
+ * @param oldAttributeTypeId The attribute type ID in the current case
+ *
+ * @return The corresponding attribute type in the portable case
+ */
+ private BlackboardAttribute.Type getNewAttributeType(BlackboardAttribute oldAttribute) throws TskCoreException {
+ BlackboardAttribute.Type oldAttrType = oldAttribute.getAttributeType();
+ if (oldAttrTypeIdToNewAttrType.containsKey(oldAttrType.getTypeID())) {
+ return oldAttrTypeIdToNewAttrType.get(oldAttrType.getTypeID());
+ }
+
+ try {
+ BlackboardAttribute.Type newCustomType = portableSkCase.addArtifactAttributeType(oldAttrType.getTypeName(),
+ oldAttrType.getValueType(), oldAttrType.getDisplayName());
+ oldAttrTypeIdToNewAttrType.put(oldAttribute.getAttributeType().getTypeID(), newCustomType);
+ return newCustomType;
+ } catch (TskDataException ex) {
+ throw new TskCoreException("Error creating new attribute type " + oldAttrType.getTypeName(), ex);
+ }
+ }
+
+ /**
+ * Top level method to copy a content object to the portable case.
+ *
+ * @param content The content object to copy
+ * @param progressPanel The progress panel
+ *
+ * @return The object ID of the copied content in the portable case
+ *
+ * @throws TskCoreException
+ */
+ @NbBundle.Messages({
+ "# {0} - File name",
+ "CreatePortableCaseModule.copyContentToPortableCase.copyingFile=Copying file {0}",
+ })
+ private long copyContentToPortableCase(Content content, ReportProgressPanel progressPanel) throws TskCoreException {
+ progressPanel.updateStatusLabel(Bundle.CreatePortableCaseModule_copyContentToPortableCase_copyingFile(content.getUniquePath()));
+
+ long newFileId;
+ CaseDbTransaction trans = portableSkCase.beginTransaction();
+ try {
+ newFileId = copyContent(content, trans);
+ trans.commit();
+ return newFileId;
+ } catch (TskCoreException ex) {
+ trans.rollback();
+ throw(ex);
+ }
+ }
+
/**
* Returns the object ID for the given content object in the portable case.
*
@@ -368,18 +584,18 @@ public class CreatePortableCaseModule implements GeneralReportModule {
Content newContent;
if (content instanceof Image) {
Image image = (Image)content;
- newContent = skCase.addImage(image.getType(), image.getSsize(), image.getSize(), image.getName(),
+ newContent = portableSkCase.addImage(image.getType(), image.getSsize(), image.getSize(), image.getName(),
new ArrayList<>(), image.getTimeZone(), image.getMd5(), image.getSha1(), image.getSha256(), image.getDeviceId(), trans);
} else if (content instanceof VolumeSystem) {
VolumeSystem vs = (VolumeSystem)content;
- newContent = skCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans);
+ newContent = portableSkCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans);
} else if (content instanceof Volume) {
Volume vs = (Volume)content;
- newContent = skCase.addVolume(parentId, vs.getAddr(), vs.getStart(), vs.getLength(),
+ newContent = portableSkCase.addVolume(parentId, vs.getAddr(), vs.getStart(), vs.getLength(),
vs.getDescription(), vs.getFlags(), trans);
} else if (content instanceof FileSystem) {
FileSystem fs = (FileSystem)content;
- newContent = skCase.addFileSystem(parentId, fs.getImageOffset(), fs.getFsType(), fs.getBlock_size(),
+ newContent = portableSkCase.addFileSystem(parentId, fs.getImageOffset(), fs.getFsType(), fs.getBlock_size(),
fs.getBlock_count(), fs.getRoot_inum(), fs.getFirst_inum(), fs.getLastInum(),
fs.getName(), trans);
} else if (content instanceof AbstractFile) {
@@ -387,10 +603,10 @@ public class CreatePortableCaseModule implements GeneralReportModule {
if (abstractFile instanceof LocalFilesDataSource) {
LocalFilesDataSource localFilesDS = (LocalFilesDataSource)abstractFile;
- newContent = skCase.addLocalFilesDataSource(localFilesDS.getDeviceId(), localFilesDS.getName(), localFilesDS.getTimeZone(), trans);
+ newContent = portableSkCase.addLocalFilesDataSource(localFilesDS.getDeviceId(), localFilesDS.getName(), localFilesDS.getTimeZone(), trans);
} else {
if (abstractFile.isDir()) {
- newContent = skCase.addLocalDirectory(parentId, abstractFile.getName(), trans);
+ newContent = portableSkCase.addLocalDirectory(parentId, abstractFile.getName(), trans);
} else {
try {
// Copy the file
@@ -410,7 +626,7 @@ public class CreatePortableCaseModule implements GeneralReportModule {
// Construct the relative path to the copied file
String relativePath = FILE_FOLDER_NAME + File.separator + exportSubFolder + File.separator + fileName;
- newContent = skCase.addLocalFile(abstractFile.getName(), relativePath, abstractFile.getSize(),
+ newContent = portableSkCase.addLocalFile(abstractFile.getName(), relativePath, abstractFile.getSize(),
abstractFile.getCtime(), abstractFile.getCrtime(), abstractFile.getAtime(), abstractFile.getMtime(),
abstractFile.getMd5Hash(), abstractFile.getKnown(), abstractFile.getMIMEType(),
true, TskData.EncodingType.NONE,
@@ -459,9 +675,9 @@ public class CreatePortableCaseModule implements GeneralReportModule {
newIdToContent.clear();
oldTagNameToNewTagName.clear();
currentCase = null;
- if (skCase != null) {
+ if (portableSkCase != null) {
// We want to close the database connections here but it is currently not possible. JIRA-4736
- skCase = null;
+ portableSkCase = null;
}
caseFolder = null;
copiedFilesFolder = null;
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java
index 2cc73c7be8..e8e38dc743 100644
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java
@@ -201,10 +201,21 @@ class AddArchiveTask implements Runnable {
success = true;
newDataSources.addAll(internalDataSource.getContent());
- // Update the names for all new data sources to be the root archive plus the name of the data source
+ // update data source info
for (Content c:internalDataSource.getContent()) {
if (c instanceof DataSource) {
DataSource ds = (DataSource) c;
+
+ // Read existing aquisition details and update them
+ String details = "Extracted from archive: " + archivePath.toString();
+ String existingDetails = ds.getAcquisitionDetails();
+ if (existingDetails != null && !existingDetails.isEmpty()) {
+ ds.setAcquisitionDetails(existingDetails + System.getProperty("line.separator") + details);
+ } else {
+ ds.setAcquisitionDetails(details);
+ }
+
+ // Update the names for all new data sources to be the root archive plus the name of the data source
String newName = Paths.get(archivePath).getFileName() + "/" + ds.getName();
ds.setDisplayName(newName);
currentCase.notifyDataSourceNameChanged(c, newName);
@@ -253,8 +264,17 @@ class AddArchiveTask implements Runnable {
archiveDspLock.wait();
- // at this point we got the content object(s) from the current DSP
+ // at this point we got the content object(s) from the current DSP.
newDataSources.addAll(internalDataSource.getContent());
+
+ for (Content c : internalDataSource.getContent()) {
+ if (c instanceof DataSource) {
+ DataSource ds = (DataSource) c;
+ // This is a new data source so just write the aquisition details
+ String details = "Extracted from archive: " + archivePath.toString();
+ ds.setAcquisitionDetails(details);
+ }
+ }
}
}
} catch (Exception ex) {
diff --git a/Testing/src/org/sleuthkit/autopsy/testing/AutopsyTestCases.java b/Testing/src/org/sleuthkit/autopsy/testing/AutopsyTestCases.java
index 96f7d18fa3..2a140bc84f 100644
--- a/Testing/src/org/sleuthkit/autopsy/testing/AutopsyTestCases.java
+++ b/Testing/src/org/sleuthkit/autopsy/testing/AutopsyTestCases.java
@@ -42,6 +42,7 @@ import org.netbeans.jellytools.NbDialogOperator;
import org.netbeans.jellytools.WizardOperator;
import org.netbeans.jemmy.JemmyProperties;
import org.netbeans.jemmy.Timeout;
+import org.netbeans.jemmy.TimeoutExpiredException;
import org.netbeans.jemmy.Timeouts;
import org.netbeans.jemmy.operators.JButtonOperator;
import org.netbeans.jemmy.operators.JCheckBoxOperator;
@@ -67,7 +68,7 @@ public class AutopsyTestCases {
private static final Logger logger = Logger.getLogger(AutopsyTestCases.class.getName());
private long start;
-
+
/**
* Escapes the slashes in a file or directory path.
*
@@ -96,274 +97,338 @@ public class AutopsyTestCases {
}
public void testNewCaseWizardOpen(String title) {
- logger.info("New Case");
- resetTimeouts("WindowWaiter.WaitWindowTimeout", 240000);
- NbDialogOperator nbdo = new NbDialogOperator(title);
- JButtonOperator jbo = new JButtonOperator(nbdo, 0); // the "New Case" button
- jbo.pushNoBlock();
+ try {
+ logger.info("New Case");
+ setTimeout("WindowWaiter.WaitWindowTimeout", 240000);
+ NbDialogOperator nbdo = new NbDialogOperator(title);
+ JButtonOperator jbo = new JButtonOperator(nbdo, 0); // the "New Case" button
+ jbo.pushNoBlock();
+ } catch (TimeoutExpiredException ex) {
+ screenshot("TimeoutScreenshot");
+ logger.log(Level.SEVERE, "AutopsyTestCases.testNewCaseWizard encountered timed out", ex);
+ }
}
public void testNewCaseWizard() {
- logger.info("New Case Wizard");
- WizardOperator wo = new WizardOperator("New Case Information");
- JTextFieldOperator jtfo0 = new JTextFieldOperator(wo, 1);
- jtfo0.typeText("AutopsyTestCase"); // Name the case "AutopsyTestCase"
- JTextFieldOperator jtfo1 = new JTextFieldOperator(wo, 2);
- jtfo1.typeText(getEscapedPath(System.getProperty("out_path")));
- wo.btNext().clickMouse();
- JTextFieldOperator jtfo2 = new JTextFieldOperator(wo, 0);
- jtfo2.typeText("000"); // Set the case number
- JTextFieldOperator jtfo3 = new JTextFieldOperator(wo, 1);
- jtfo3.typeText("Examiner 1"); // Set the case examiner
- start = System.currentTimeMillis();
- wo.btFinish().clickMouse();
+ try {
+ logger.info("New Case Wizard");
+ WizardOperator wo = new WizardOperator("New Case Information");
+ JTextFieldOperator jtfo0 = new JTextFieldOperator(wo, 1);
+ jtfo0.typeText("AutopsyTestCase"); // Name the case "AutopsyTestCase"
+ JTextFieldOperator jtfo1 = new JTextFieldOperator(wo, 2);
+ jtfo1.typeText(getEscapedPath(System.getProperty("out_path")));
+ wo.btNext().clickMouse();
+ JTextFieldOperator jtfo2 = new JTextFieldOperator(wo, 0);
+ jtfo2.typeText("000"); // Set the case number
+ JTextFieldOperator jtfo3 = new JTextFieldOperator(wo, 1);
+ jtfo3.typeText("Examiner 1"); // Set the case examiner
+ start = System.currentTimeMillis();
+ wo.btFinish().clickMouse();
+ } catch (TimeoutExpiredException ex) {
+ screenshot("TimeoutScreenshot");
+ logger.log(Level.SEVERE, "AutopsyTestCases.testNewCaseWizard encountered timed out", ex);
+ }
}
public void testStartAddImageFileDataSource() {
- /*
- * This time out is to give time for creating case database and opening solr index
- */
- new Timeout("pausing", 120000).sleep();
- logger.info("Starting Add Image process");
- resetTimeouts("WindowWaiter.WaitWindowTimeOut", 240000);
- WizardOperator wo = new WizardOperator("Add Data Source");
- while(!wo.btNext().isEnabled()){
- new Timeout("pausing", 1000).sleep(); // give it a second till the Add Data Source dialog enabled
+ try {
+ /*
+ * This time out is to give time for creating case database and
+ * opening solr index
+ */
+ new Timeout("pausing", 120000).sleep();
+ logger.info("Starting Add Image process");
+ setTimeout("WindowWaiter.WaitWindowTimeOut", 240000);
+ WizardOperator wo = new WizardOperator("Add Data Source");
+ while (!wo.btNext().isEnabled()) {
+ new Timeout("pausing", 1000).sleep(); // give it a second till the Add Data Source dialog enabled
+ }
+ //select the toggle button for Disk Image or VM File it will be the first button created and proceed to next panel
+ JToggleButtonOperator jtbo = new JToggleButtonOperator(wo, 0);
+ jtbo.clickMouse();
+ wo.btNext().clickMouse();
+ JTextFieldOperator jtfo0 = new JTextFieldOperator(wo, 0);
+ String img_path = getEscapedPath(System.getProperty("img_path"));
+ String imageDir = img_path;
+ ((JTextComponent) jtfo0.getSource()).setText(imageDir);
+ JComboBoxOperator comboBoxOperator = new JComboBoxOperator(wo, 0);
+ comboBoxOperator.setSelectedItem("(GMT-5:00) America/New_York");
+ wo.btNext().clickMouse();
+ } catch (TimeoutExpiredException ex) {
+ screenshot("TimeoutScreenshot");
+ logger.log(Level.SEVERE, "AutopsyTestCases.testNewCaseWizard encountered timed out", ex);
}
- //select the toggle button for Disk Image or VM File it will be the first button created and proceed to next panel
- JToggleButtonOperator jtbo = new JToggleButtonOperator(wo, 0);
- jtbo.clickMouse();
- wo.btNext().clickMouse();
- JTextFieldOperator jtfo0 = new JTextFieldOperator(wo, 0);
- String img_path = getEscapedPath(System.getProperty("img_path"));
- String imageDir = img_path;
- ((JTextComponent) jtfo0.getSource()).setText(imageDir);
- JComboBoxOperator comboBoxOperator = new JComboBoxOperator(wo, 0);
- comboBoxOperator.setSelectedItem("(GMT-5:00) America/New_York");
- wo.btNext().clickMouse();
}
public void testStartAddLogicalFilesDataSource() {
- /*
- * This time out is to give time for creating case database and opening solr index
- */
- new Timeout("pausing", 120000).sleep();
- logger.info("Starting Add Logical Files process");
- WizardOperator wo = new WizardOperator("Add Data Source");
- wo.setTimeouts(resetTimeouts("WindowWaiter.WaitWindowTimeOut", 240000));
- while(!wo.btNext().isEnabled()){
- new Timeout("pausing", 1000).sleep(); // give it a second till the Add Data Source dialog enabled
+ try {
+ /*
+ * This time out is to give time for creating case database and
+ * opening solr index
+ */
+ new Timeout("pausing", 120000).sleep();
+ logger.info("Starting Add Logical Files process");
+ WizardOperator wo = new WizardOperator("Add Data Source");
+ wo.setTimeouts(setTimeout("WindowWaiter.WaitWindowTimeOut", 240000));
+ while (!wo.btNext().isEnabled()) {
+ new Timeout("pausing", 1000).sleep(); // give it a second till the Add Data Source dialog enabled
+ }
+ //select the toggle button for Logical Files it will be the third button created and proceed to next panel
+ JToggleButtonOperator jtbo = new JToggleButtonOperator(wo, 2);
+ jtbo.clickMouse();
+ wo.btNext().clickMouse();
+ JButtonOperator addButtonOperator = new JButtonOperator(wo, "Add");
+ addButtonOperator.pushNoBlock();
+ JFileChooserOperator fileChooserOperator = new JFileChooserOperator();
+ fileChooserOperator.setCurrentDirectory(new File(getEscapedPath(System.getProperty("img_path"))));
+ // set the current directory one level above the directory containing logicalFileSet folder.
+ fileChooserOperator.goUpLevel();
+ fileChooserOperator.chooseFile(new File(getEscapedPath(System.getProperty("img_path"))).getName());
+ wo.btNext().clickMouse();
+ } catch (TimeoutExpiredException ex) {
+ screenshot("TimeoutScreenshot");
+ logger.log(Level.SEVERE, "AutopsyTestCases.testNewCaseWizard encountered timed out", ex);
}
- //select the toggle button for Logical Files it will be the third button created and proceed to next panel
- JToggleButtonOperator jtbo = new JToggleButtonOperator(wo, 2);
- jtbo.clickMouse();
- wo.btNext().clickMouse();
- JButtonOperator addButtonOperator = new JButtonOperator(wo, "Add");
- addButtonOperator.pushNoBlock();
- JFileChooserOperator fileChooserOperator = new JFileChooserOperator();
- fileChooserOperator.setCurrentDirectory(new File(getEscapedPath(System.getProperty("img_path"))));
- // set the current directory one level above the directory containing logicalFileSet folder.
- fileChooserOperator.goUpLevel();
- fileChooserOperator.chooseFile(new File(getEscapedPath(System.getProperty("img_path"))).getName());
- wo.btNext().clickMouse();
}
public void testAddSourceWizard1() {
- WizardOperator wo = new WizardOperator("Add Data Source");
- while (!wo.btFinish().isEnabled()) {
- new Timeout("pausing", 1000).sleep(); // give it a second (or five) to process
+ try {
+ WizardOperator wo = new WizardOperator("Add Data Source");
+ while (!wo.btFinish().isEnabled()) {
+ new Timeout("pausing", 1000).sleep(); // give it a second (or five) to process
+ }
+ logger.log(Level.INFO, "Add image took {0}ms", (System.currentTimeMillis() - start));
+ wo.btFinish().clickMouse();
+ } catch (TimeoutExpiredException ex) {
+ screenshot("TimeoutScreenshot");
+ logger.log(Level.SEVERE, "AutopsyTestCases.testNewCaseWizard encountered timed out", ex);
}
- logger.log(Level.INFO, "Add image took {0}ms", (System.currentTimeMillis() - start));
- wo.btFinish().clickMouse();
}
public void testConfigureIngest1() {
- /*
- * This timeout is to allow the setup for the ingest job settings panel
- * to complete.
- */
- new Timeout("pausing", 10000).sleep();
+ try {
+ /*
+ * This timeout is to allow the setup for the ingest job settings
+ * panel to complete.
+ */
+ new Timeout("pausing", 10000).sleep();
- logger.info("Looking for hash lookup module in ingest job settings panel");
- WizardOperator wo = new WizardOperator("Add Data Source");
- while(!wo.btNext().isEnabled()){
- new Timeout("pausing", 1000).sleep(); // give it a second till the Add Data Source dialog enabled
+ logger.info("Looking for hash lookup module in ingest job settings panel");
+ WizardOperator wo = new WizardOperator("Add Data Source");
+ while (!wo.btNext().isEnabled()) {
+ new Timeout("pausing", 1000).sleep(); // give it a second till the Add Data Source dialog enabled
+ }
+ JTableOperator jto = new JTableOperator(wo, 0);
+ int row = jto.findCellRow("Hash Lookup", 2, 0);
+ jto.clickOnCell(row, 1);
+ logger.info("Selected hash lookup module in ingest job settings panel");
+ JButtonOperator jbo1 = new JButtonOperator(wo, "Global Settings");
+ jbo1.pushNoBlock();
+ logger.info("Pushed Global Settings button for hash lookup module in ingest job settings panel");
+ } catch (TimeoutExpiredException ex) {
+ screenshot("TimeoutScreenshot");
+ logger.log(Level.SEVERE, "AutopsyTestCases.testNewCaseWizard encountered timed out", ex);
}
- JTableOperator jto = new JTableOperator(wo, 0);
- int row = jto.findCellRow("Hash Lookup", 2, 0);
- jto.clickOnCell(row, 1);
- logger.info("Selected hash lookup module in ingest job settings panel");
- JButtonOperator jbo1 = new JButtonOperator(wo, "Global Settings");
- jbo1.pushNoBlock();
- logger.info("Pushed Global Settings button for hash lookup module in ingest job settings panel");
}
public void testConfigureHash() {
- logger.info("Hash Configure");
- JDialog hashMainDialog = JDialogOperator.waitJDialog("Global Hash Lookup Settings", false, false);
- JDialogOperator hashMainDialogOperator = new JDialogOperator(hashMainDialog);
- List databases = new ArrayList<>();
- databases.add(getEscapedPath(System.getProperty("nsrl_path")));
- databases.add(getEscapedPath(System.getProperty("known_bad_path")));
- databases.stream().map((database) -> {
- JButtonOperator importButtonOperator = new JButtonOperator(hashMainDialogOperator, "Import");
- importButtonOperator.pushNoBlock();
- JDialog addDatabaseDialog = JDialogOperator.waitJDialog("Import Hash Set", false, false);
- JDialogOperator addDatabaseDialogOperator = new JDialogOperator(addDatabaseDialog);
- JButtonOperator browseButtonOperator = new JButtonOperator(addDatabaseDialogOperator, "Open...", 0);
- browseButtonOperator.pushNoBlock();
- JFileChooserOperator fileChooserOperator = new JFileChooserOperator();
- fileChooserOperator.chooseFile(database);
- JButtonOperator okButtonOperator = new JButtonOperator(addDatabaseDialogOperator, "OK", 0);
- return okButtonOperator;
- }).map((okButtonOperator) -> {
- okButtonOperator.pushNoBlock();
- return okButtonOperator;
- }).forEach((_item) -> {
+ try {
+ logger.info("Hash Configure");
+ JDialog hashMainDialog = JDialogOperator.waitJDialog("Global Hash Lookup Settings", false, false);
+ JDialogOperator hashMainDialogOperator = new JDialogOperator(hashMainDialog);
+ List databases = new ArrayList<>();
+ databases.add(getEscapedPath(System.getProperty("nsrl_path")));
+ databases.add(getEscapedPath(System.getProperty("known_bad_path")));
+ databases.stream().map((database) -> {
+ JButtonOperator importButtonOperator = new JButtonOperator(hashMainDialogOperator, "Import");
+ importButtonOperator.pushNoBlock();
+ JDialog addDatabaseDialog = JDialogOperator.waitJDialog("Import Hash Set", false, false);
+ JDialogOperator addDatabaseDialogOperator = new JDialogOperator(addDatabaseDialog);
+ JButtonOperator browseButtonOperator = new JButtonOperator(addDatabaseDialogOperator, "Open...", 0);
+ browseButtonOperator.pushNoBlock();
+ JFileChooserOperator fileChooserOperator = new JFileChooserOperator();
+ fileChooserOperator.chooseFile(database);
+ JButtonOperator okButtonOperator = new JButtonOperator(addDatabaseDialogOperator, "OK", 0);
+ return okButtonOperator;
+ }).map((okButtonOperator) -> {
+ okButtonOperator.pushNoBlock();
+ return okButtonOperator;
+ }).forEach((_item) -> {
+ new Timeout("pausing", 1000).sleep(); // give it a second (or five) to process
+ });
+ // Used if the database has no index
+ //JDialog jd3 = JDialogOperator.waitJDialog("No Index Exists", false, false);
+ //JDialogOperator jdo3 = new JDialogOperator(jd3);
+ //JButtonOperator jbo3 = new JButtonOperator(jdo3, "Yes", 0);
new Timeout("pausing", 1000).sleep(); // give it a second (or five) to process
- });
- // Used if the database has no index
- //JDialog jd3 = JDialogOperator.waitJDialog("No Index Exists", false, false);
- //JDialogOperator jdo3 = new JDialogOperator(jd3);
- //JButtonOperator jbo3 = new JButtonOperator(jdo3, "Yes", 0);
- new Timeout("pausing", 1000).sleep(); // give it a second (or five) to process
- //jbo3.pushNoBlock();
- JButtonOperator jbo4 = new JButtonOperator(hashMainDialogOperator, "OK", 0);
- jbo4.pushNoBlock();
+ //jbo3.pushNoBlock();
+ JButtonOperator jbo4 = new JButtonOperator(hashMainDialogOperator, "OK", 0);
+ jbo4.pushNoBlock();
+ } catch (TimeoutExpiredException ex) {
+ screenshot("TimeoutScreenshot");
+ logger.log(Level.SEVERE, "AutopsyTestCases.testNewCaseWizard encountered timed out", ex);
+ }
}
public void testConfigureIngest2() {
- logger.info("Looking for keyword search module in ingest job settings panel");
- WizardOperator wo = new WizardOperator("Add Data Source");
- while(!wo.btNext().isEnabled()){
- new Timeout("pausing", 1000).sleep(); // give it a second till the Add Data Source dialog enabled
+ try {
+ logger.info("Looking for keyword search module in ingest job settings panel");
+ WizardOperator wo = new WizardOperator("Add Data Source");
+ while (!wo.btNext().isEnabled()) {
+ new Timeout("pausing", 1000).sleep(); // give it a second till the Add Data Source dialog enabled
+ }
+ JTableOperator jto = new JTableOperator(wo, 0);
+ int row = jto.findCellRow("Keyword Search", 2, 0);
+ jto.clickOnCell(row, 1);
+ logger.info("Selected keyword search module in ingest job settings panel");
+ JButtonOperator jbo1 = new JButtonOperator(wo, "Global Settings");
+ jbo1.pushNoBlock();
+ logger.info("Pushed Global Settings button for keyword search module in ingest job settings panel");
+ } catch (TimeoutExpiredException ex) {
+ screenshot("TimeoutScreenshot");
+ logger.log(Level.SEVERE, "AutopsyTestCases.testNewCaseWizard encountered timed out", ex);
}
- JTableOperator jto = new JTableOperator(wo, 0);
- int row = jto.findCellRow("Keyword Search", 2, 0);
- jto.clickOnCell(row, 1);
- logger.info("Selected keyword search module in ingest job settings panel");
- JButtonOperator jbo1 = new JButtonOperator(wo, "Global Settings");
- jbo1.pushNoBlock();
- logger.info("Pushed Global Settings button for keyword search module in ingest job settings panel");
}
public void testConfigureSearch() {
- logger.info("Search Configure");
- JDialog jd = JDialogOperator.waitJDialog("Global Keyword Search Settings", false, false);
- JDialogOperator jdo = new JDialogOperator(jd);
- String words = getEscapedPath(System.getProperty("keyword_path"));
- JButtonOperator jbo0 = new JButtonOperator(jdo, "Import List", 0);
- jbo0.pushNoBlock();
- JFileChooserOperator jfco0 = new JFileChooserOperator();
- jfco0.chooseFile(words);
- JTableOperator jto = new JTableOperator(jdo, 0);
- jto.clickOnCell(0, 0);
- new Timeout("pausing", 1000).sleep(); // give it a second to process
- if (Boolean.parseBoolean(System.getProperty("mugen_mode"))) {
- JTabbedPaneOperator jtpo = new JTabbedPaneOperator(jdo);
- jtpo.selectPage("String Extraction");
- JCheckBoxOperator jcbo0 = new JCheckBoxOperator(jtpo, "Arabic (Arabic)");
- jcbo0.doClick();
- JCheckBoxOperator jcbo1 = new JCheckBoxOperator(jtpo, "Han (Chinese, Japanese, Korean)");
- jcbo1.doClick();
+ try {
+ logger.info("Search Configure");
+ JDialog jd = JDialogOperator.waitJDialog("Global Keyword Search Settings", false, false);
+ JDialogOperator jdo = new JDialogOperator(jd);
+ String words = getEscapedPath(System.getProperty("keyword_path"));
+ JButtonOperator jbo0 = new JButtonOperator(jdo, "Import List", 0);
+ jbo0.pushNoBlock();
+ JFileChooserOperator jfco0 = new JFileChooserOperator();
+ jfco0.chooseFile(words);
+ JTableOperator jto = new JTableOperator(jdo, 0);
+ jto.clickOnCell(0, 0);
new Timeout("pausing", 1000).sleep(); // give it a second to process
+ if (Boolean.parseBoolean(System.getProperty("mugen_mode"))) {
+ JTabbedPaneOperator jtpo = new JTabbedPaneOperator(jdo);
+ jtpo.selectPage("String Extraction");
+ JCheckBoxOperator jcbo0 = new JCheckBoxOperator(jtpo, "Arabic (Arabic)");
+ jcbo0.doClick();
+ JCheckBoxOperator jcbo1 = new JCheckBoxOperator(jtpo, "Han (Chinese, Japanese, Korean)");
+ jcbo1.doClick();
+ new Timeout("pausing", 1000).sleep(); // give it a second to process
+ }
+ JButtonOperator jbo2 = new JButtonOperator(jdo, "OK", 0);
+ jbo2.pushNoBlock();
+ WizardOperator wo = new WizardOperator("Add Data Source");
+ new Timeout("pausing", 10000).sleep(); // let things catch up
+ wo.btNext().clickMouse();
+ } catch (TimeoutExpiredException ex) {
+ screenshot("TimeoutScreenshot");
+ logger.log(Level.SEVERE, "AutopsyTestCases.testNewCaseWizard encountered timed out", ex);
}
- JButtonOperator jbo2 = new JButtonOperator(jdo, "OK", 0);
- jbo2.pushNoBlock();
- WizardOperator wo = new WizardOperator("Add Data Source");
- new Timeout("pausing", 10000).sleep(); // let things catch up
- wo.btNext().clickMouse();
}
public void testIngest() {
- logger.info("Ingest 3");
- new Timeout("pausing", 10000).sleep(); // wait for ingest to actually start
- long startIngest = System.currentTimeMillis();
- IngestManager man = IngestManager.getInstance();
- while (man.isIngestRunning()) {
- new Timeout("pausing", 1000).sleep(); // give it a second (or five) to process
+ try {
+ logger.info("Ingest 3");
+ new Timeout("pausing", 10000).sleep(); // wait for ingest to actually start
+ long startIngest = System.currentTimeMillis();
+ IngestManager man = IngestManager.getInstance();
+ while (man.isIngestRunning()) {
+ new Timeout("pausing", 1000).sleep(); // give it a second (or five) to process
+ }
+ logger.log(Level.INFO, "Ingest (including enqueue) took {0}ms", (System.currentTimeMillis() - startIngest));
+ // allow keyword search to finish saving artifacts, just in case
+ // but randomize the timing so that we don't always get the same error
+ // consistently, making it seem like default behavior
+ Random rand = new Random();
+ new Timeout("pausing", 10000 + (rand.nextInt(15000) + 5000)).sleep();
+ } catch (TimeoutExpiredException ex) {
+ screenshot("TimeoutScreenshot");
+ logger.log(Level.SEVERE, "AutopsyTestCases.testNewCaseWizard encountered timed out", ex);
}
- logger.log(Level.INFO, "Ingest (including enqueue) took {0}ms", (System.currentTimeMillis() - startIngest));
- // allow keyword search to finish saving artifacts, just in case
- // but randomize the timing so that we don't always get the same error
- // consistently, making it seem like default behavior
- Random rand = new Random();
- new Timeout("pausing", 10000 + (rand.nextInt(15000) + 5000)).sleep();
- screenshot("Finished Ingest");
}
public void testExpandDataSourcesTree() {
- logger.info("Data Sources Node");
- MainWindowOperator mwo = MainWindowOperator.getDefault();
- JTreeOperator jto = new JTreeOperator(mwo, "Data Sources");
- String [] nodeNames = {"Data Sources"};
- TreePath tp = jto.findPath(nodeNames);
- expandNodes(jto, tp);
- screenshot("Data Sources Tree");
+ try {
+ logger.info("Data Sources Node");
+ MainWindowOperator mwo = MainWindowOperator.getDefault();
+ JTreeOperator jto = new JTreeOperator(mwo, "Data Sources");
+ String[] nodeNames = {"Data Sources"};
+ TreePath tp = jto.findPath(nodeNames);
+ expandNodes(jto, tp);
+ } catch (TimeoutExpiredException ex) {
+ screenshot("TimeoutScreenshot");
+ logger.log(Level.SEVERE, "AutopsyTestCases.testNewCaseWizard encountered timed out", ex);
+ }
}
public void testGenerateReportToolbar() {
- logger.info("Generate Report Toolbars");
- MainWindowOperator mwo = MainWindowOperator.getDefault();
- JButtonOperator jbo = new JButtonOperator(mwo, "Generate Report");
- jbo.pushNoBlock();
- new Timeout("pausing", 5000).sleep();
+ try {
+ logger.info("Generate Report Toolbars");
+ MainWindowOperator mwo = MainWindowOperator.getDefault();
+ JButtonOperator jbo = new JButtonOperator(mwo, "Generate Report");
+ jbo.pushNoBlock();
+ } catch (TimeoutExpiredException ex) {
+ screenshot("TimeoutScreenshot");
+ logger.log(Level.SEVERE, "AutopsyTestCases.testNewCaseWizard encountered timed out", ex);
+ }
}
public void testGenerateReportButton() throws IOException {
- logger.info("Generate Report Button");
- resetTimeouts("ComponentOperator.WaitComponentTimeout", 240000);
- JDialog reportDialog = JDialogOperator.waitJDialog("Generate Report", false, false);
- JDialogOperator reportDialogOperator = new JDialogOperator(reportDialog);
- JListOperator listOperator = new JListOperator(reportDialogOperator);
- JButtonOperator jbo0 = new JButtonOperator(reportDialogOperator, "Next");
- DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss");
- Date date = new Date();
- String datenotime = dateFormat.format(date);
- listOperator.clickOnItem(0, 1);
- jbo0.pushNoBlock();
- new Timeout("pausing", 2000).sleep();
- JButtonOperator jbo1 = new JButtonOperator(reportDialogOperator, "Finish");
- jbo1.pushNoBlock();
- JDialog previewDialog = JDialogOperator.waitJDialog("Progress", false, false);
- screenshot("Progress");
- JDialogOperator previewDialogOperator = new JDialogOperator(previewDialog);
- JLabelOperator.waitJLabel(previewDialog, "Complete", false, false);
- JButtonOperator jbo2 = new JButtonOperator(previewDialogOperator, "Close");
- jbo2.pushNoBlock();
- new Timeout("pausing", 10000).sleep();
- System.setProperty("ReportStr", datenotime);
- screenshot("Done Testing");
+ try {
+ logger.info("Generate Report Button");
+ setTimeout("ComponentOperator.WaitComponentTimeout", 240000);
+ JDialog reportDialog = JDialogOperator.waitJDialog("Generate Report", false, false);
+ JDialogOperator reportDialogOperator = new JDialogOperator(reportDialog);
+ JListOperator listOperator = new JListOperator(reportDialogOperator);
+ JButtonOperator jbo0 = new JButtonOperator(reportDialogOperator, "Next");
+ DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss");
+ Date date = new Date();
+ String datenotime = dateFormat.format(date);
+ listOperator.clickOnItem(0, 1);
+ jbo0.pushNoBlock();
+ new Timeout("pausing", 2000).sleep();
+ JButtonOperator jbo1 = new JButtonOperator(reportDialogOperator, "Finish");
+ jbo1.pushNoBlock();
+ JDialog previewDialog = JDialogOperator.waitJDialog("Progress", false, false);
+ JDialogOperator previewDialogOperator = new JDialogOperator(previewDialog);
+ JLabelOperator.waitJLabel(previewDialog, "Complete", false, false);
+ JButtonOperator jbo2 = new JButtonOperator(previewDialogOperator, "Close");
+ jbo2.pushNoBlock();
+ new Timeout("pausing", 10000).sleep();
+ System.setProperty("ReportStr", datenotime);
+ } catch (TimeoutExpiredException ex) {
+ screenshot("TimeoutScreenshot");
+ logger.log(Level.SEVERE, "AutopsyTestCases.testNewCaseWizard encountered timed out", ex);
+ }
}
public void screenshot(String name) {
- logger.info("Taking screenshot.");
- try {
- Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
- BufferedImage capture = new Robot().createScreenCapture(screenRect);
- String outPath = getEscapedPath(System.getProperty("out_path"));
- ImageIO.write(capture, "png", new File(outPath + "\\" + name + ".png"));
- new Timeout("pausing", 1000).sleep(); // give it a second to save
- } catch (IOException ex) {
- logger.log(Level.WARNING, "IOException taking screenshot.", ex);
- } catch (AWTException ex) {
- logger.log(Level.WARNING, "AWTException taking screenshot.", ex);
-
+ String outPath = getEscapedPath(System.getProperty("out_path"));
+ File screenShotFile = new File(outPath + "\\" + name + ".png");
+ if (!screenShotFile.exists()) {
+ logger.info("Taking screenshot.");
+ try {
+ Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
+ BufferedImage capture = new Robot().createScreenCapture(screenRect);
+ ImageIO.write(capture, "png", screenShotFile);
+ new Timeout("pausing", 1000).sleep(); // give it a second to save
+ } catch (IOException ex) {
+ logger.log(Level.WARNING, "IOException taking screenshot.", ex);
+ } catch (AWTException ex) {
+ logger.log(Level.WARNING, "AWTException taking screenshot.", ex);
+ }
}
}
-
- /*
- * Nightly test failed at WindowWaiter.WaitWindowTimeOut because of TimeoutExpiredException. So we
- * use this conveninent method to override the default Jemmy Timeouts value.
- */
- private Timeouts resetTimeouts(String name, int value) {
+ /*
+ * Nightly test failed at WindowWaiter.WaitWindowTimeOut because of
+ * TimeoutExpiredException. So we use this conveninent method to override
+ * the default Jemmy Timeouts value.
+ */
+ private Timeouts setTimeout(String name, int value) {
Timeouts timeouts = JemmyProperties.getCurrentTimeouts();
timeouts.setTimeout(name, value);
return timeouts;
}
-
+
private void setMultiUserPerferences() {
UserPreferences.setIsMultiUserModeEnabled(true);
//PostgreSQL database settings
@@ -393,8 +458,8 @@ public class AutopsyTestCases {
logger.log(Level.SEVERE, "Error saving messaging service connection info", ex); //NON-NLS
}
}
-
- private void expandNodes (JTreeOperator jto, TreePath tp) {
+
+ private void expandNodes(JTreeOperator jto, TreePath tp) {
try {
jto.expandPath(tp);
for (TreePath t : jto.getChildPaths(tp)) {