diff --git a/Core/ivy.xml b/Core/ivy.xml
index 35cb8de904..6d67fb0992 100644
--- a/Core/ivy.xml
+++ b/Core/ivy.xml
@@ -15,8 +15,8 @@
-
-
+
+
diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties
index ee7a175733..9adbad3e9a 100644
--- a/Core/nbproject/project.properties
+++ b/Core/nbproject/project.properties
@@ -1,6 +1,6 @@
file.reference.activemq-all-5.11.1.jar=release/modules/ext/activemq-all-5.11.1.jar
file.reference.c3p0-0.9.5.jar=release/modules/ext/c3p0-0.9.5.jar
-file.reference.commons-compress-1.12.jar=release/modules/ext/commons-compress-1.12.jar
+file.reference.commons-compress-1.14.jar=release/modules/ext/commons-compress-1.14.jar
file.reference.commons-dbcp2-2.1.1.jar=release\\modules\\ext\\commons-dbcp2-2.1.1.jar
file.reference.commons-pool2-2.4.2.jar=release\\modules\\ext\\commons-pool2-2.4.2.jar
file.reference.jdom-2.0.5-contrib.jar=release/modules/ext/jdom-2.0.5-contrib.jar
@@ -8,7 +8,7 @@ file.reference.jdom-2.0.5.jar=release/modules/ext/jdom-2.0.5.jar
file.reference.jsoup-1.10.3.jar=release/modules/ext/jsoup-1.10.3.jar
file.reference.jython-standalone-2.7.0.jar=release/modules/ext/jython-standalone-2.7.0.jar
file.reference.mchange-commons-java-0.2.9.jar=release/modules/ext/mchange-commons-java-0.2.9.jar
-file.reference.metadata-extractor-2.9.1.jar=release/modules/ext/metadata-extractor-2.9.1.jar
+file.reference.metadata-extractor-2.10.1.jar=release/modules/ext/metadata-extractor-2.10.1.jar
file.reference.postgresql-9.4.1211.jre7.jar=release/modules/ext/postgresql-9.4.1211.jre7.jar
file.reference.opencv-248.jar=release/modules/ext/opencv-248.jar
file.reference.Rejistry-1.0-SNAPSHOT.jar=release/modules/ext/Rejistry-1.0-SNAPSHOT.jar
@@ -16,14 +16,14 @@ file.reference.sevenzipjbinding-AllPlatforms.jar=release/modules/ext/sevenzipjbi
file.reference.sevenzipjbinding.jar=release/modules/ext/sevenzipjbinding.jar
file.reference.sqlite-jdbc-3.8.11.jar=release/modules/ext/sqlite-jdbc-3.8.11.jar
file.reference.StixLib.jar=release/modules/ext/StixLib.jar
-file.reference.tika-core-1.14.jar=release/modules/ext/tika-core-1.14.jar
-file.reference.tika-parsers-1.14.jar=release/modules/ext/tika-parsers-1.14.jar
file.reference.sleuthkit-postgresql-4.6.0.jar=release/modules/ext/sleuthkit-postgresql-4.6.0.jar
-file.reference.xmpcore-5.1.2.jar=release/modules/ext/xmpcore-5.1.2.jar
+file.reference.tika-core-1.17.jar=release/modules/ext/tika-core-1.17.jar
+file.reference.tika-parsers-1.17.jar=release/modules/ext/tika-parsers-1.17.jar
file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar
file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar
file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar
-file.reference.xz-1.5.jar=release/modules/ext/xz-1.5.jar
+file.reference.xmpcore-5.1.3.jar=release/modules/ext/xmpcore-5.1.3.jar
+file.reference.xz-1.6.jar=release/modules/ext/xz-1.6.jar
file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar
javac.source=1.8
javac.compilerargs=-Xlint -Xlint:-serial
diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml
index a05219b32e..53471a3396 100644
--- a/Core/nbproject/project.xml
+++ b/Core/nbproject/project.xml
@@ -329,10 +329,6 @@
ext/jdom-2.0.5.jar
release/modules/ext/jdom-2.0.5.jar
-
- ext/tika-core-1.14.jar
- release/modules/ext/tika-core-1.14.jar
-
ext/sleuthkit-postgresql-4.6.0.jar
release/modules/ext/sleuthkit-postgresql-4.6.0.jar
@@ -346,12 +342,12 @@
release/modules/ext/curator-framework-2.8.0.jar
- ext/commons-dbcp2-2.1.1.jar
- release\modules\ext\commons-dbcp2-2.1.1.jar
+ ext/commons-compress-1.14.jar
+ release/modules/ext/commons-compress-1.14.jar
- ext/tika-parsers-1.14.jar
- release/modules/ext/tika-parsers-1.14.jar
+ ext/commons-dbcp2-2.1.1.jar
+ release\modules\ext\commons-dbcp2-2.1.1.jar
ext/jython-standalone-2.7.0.jar
@@ -374,12 +370,12 @@
release/modules/ext/curator-recipes-2.8.0.jar
- ext/xz-1.5.jar
- release/modules/ext/xz-1.5.jar
+ ext/metadata-extractor-2.10.1.jar
+ release/modules/ext/metadata-extractor-2.10.1.jar
- ext/xmpcore-5.1.2.jar
- release/modules/ext/xmpcore-5.1.2.jar
+ ext/tika-core-1.17.jar
+ release/modules/ext/tika-core-1.17.jar
ext/StixLib.jar
@@ -389,6 +385,10 @@
ext/curator-client-2.8.0.jar
release/modules/ext/curator-client-2.8.0.jar
+
+ ext/tika-parsers-1.17.jar
+ release/modules/ext/tika-parsers-1.17.jar
+
ext/sqlite-jdbc-3.8.11.jar
release/modules/ext/sqlite-jdbc-3.8.11.jar
@@ -397,6 +397,10 @@
ext/activemq-all-5.11.1.jar
release/modules/ext/activemq-all-5.11.1.jar
+
+ ext/xz-1.6.jar
+ release/modules/ext/xz-1.6.jar
+
ext/Rejistry-1.0-SNAPSHOT.jar
release/modules/ext/Rejistry-1.0-SNAPSHOT.jar
@@ -409,14 +413,6 @@
ext/commons-pool2-2.4.2.jar
release\modules\ext\commons-pool2-2.4.2.jar
-
- ext/metadata-extractor-2.9.1.jar
- release/modules/ext/metadata-extractor-2.9.1.jar
-
-
- ext/commons-compress-1.12.jar
- release/modules/ext/commons-compress-1.12.jar
-
ext/jsoup-1.10.3.jar
release/modules/ext/jsoup-1.10.3.jar
@@ -429,6 +425,10 @@
ext/c3p0-0.9.5.jar
release/modules/ext/c3p0-0.9.5.jar
+
+ ext/xmpcore-5.1.3.jar
+ release/modules/ext/xmpcore-5.1.3.jar
+
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties
index 1cc822fea5..ab2b5c12dc 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties
@@ -223,17 +223,11 @@ ReviewModeCasePanel.MetadataFileHeaderText=Metadata File
OpenMultiUserCasePanel.jLabel1.text=Recent Cases
OpenMultiUserCasePanel.openButton.text=Open
OpenMultiUserCasePanel.cancelButton.text=Cancel
-MultiUserCasesPanel.rbWeeks.text=Weeks
-MultiUserCasesPanel.rbDays.text=Days
-MultiUserCasesPanel.bnShowLog.toolTipText=Display case log file for selected case
-MultiUserCasesPanel.bnShowLog.text=&Show Auto Ingest Case Log
-MultiUserCasesPanel.rbAllCases.text=Everything
-MultiUserCasesPanel.bnRefresh.text=&Refresh
MultiUserCasesPanel.bnOpen.text=&Open
-MultiUserCasesPanel.rbGroupLabel.text=Show cases created in the last 10:
-MultiUserCasesPanel.rbMonths.text=Months
CueBannerPanel.newCaseLabel.text=New Case
CueBannerPanel.openCaseButton.text=
CueBannerPanel.openCaseLabel.text=Open Case
MultiUserCasesPanel.bnOpenSingleUserCase.text=Open Single-User Case...
CueBannerPanel.newCaseButton.text=
+MultiUserCasesPanel.searchLabel.text=Start typing to search by case name
+MultiUserCasesPanel.cancelButton.text=Cancel
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseBrowser.form b/Core/src/org/sleuthkit/autopsy/casemodule/CaseBrowser.form
new file mode 100644
index 0000000000..edda7a749c
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseBrowser.form
@@ -0,0 +1,47 @@
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseBrowser.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseBrowser.java
new file mode 100644
index 0000000000..9440ca1356
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseBrowser.java
@@ -0,0 +1,296 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2017-2018 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.casemodule;
+
+import java.awt.Component;
+import java.lang.reflect.InvocationTargetException;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumnModel;
+import org.netbeans.swing.etable.ETableColumn;
+import org.netbeans.swing.etable.ETableColumnModel;
+import org.netbeans.swing.outline.DefaultOutlineModel;
+import org.netbeans.swing.outline.Outline;
+import org.openide.nodes.Node;
+import java.awt.EventQueue;
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import javax.swing.SwingWorker;
+import org.openide.explorer.ExplorerManager;
+import org.openide.util.NbBundle;
+import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
+import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.datamodel.EmptyNode;
+
+/**
+ * A Swing JPanel with a scroll pane child component. The scroll pane contain
+ * the table of cases.
+ *
+ * Used to display a list of multi user cases and allow the user to open one of
+ * them.
+ *
+ */
+class CaseBrowser extends javax.swing.JPanel implements ExplorerManager.Provider {
+
+ private static final long serialVersionUID = 1L;
+ private final Outline outline;
+ private ExplorerManager em;
+ private final org.openide.explorer.view.OutlineView outlineView;
+ private int originalPathColumnIndex = 0;
+ private static final Logger LOGGER = Logger.getLogger(CaseBrowser.class.getName());
+ private LoadCaseMapWorker tableWorker;
+
+ @Override
+ public ExplorerManager getExplorerManager() {
+ return em;
+ }
+
+ /**
+ * Creates a new CaseBrowser
+ */
+ CaseBrowser() {
+ outlineView = new org.openide.explorer.view.OutlineView();
+ initComponents();
+
+ outline = outlineView.getOutline();
+ outlineView.setPropertyColumns(
+ Bundle.CaseNode_column_createdTime(), Bundle.CaseNode_column_createdTime(),
+ Bundle.CaseNode_column_status(), Bundle.CaseNode_column_status(),
+ Bundle.CaseNode_column_metadataFilePath(), Bundle.CaseNode_column_metadataFilePath());
+ ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.CaseNode_column_name());
+ customize();
+
+ }
+
+ /**
+ * Configures the the table of cases and its columns.
+ */
+ private void customize() {
+ outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ TableColumnModel columnModel = outline.getColumnModel();
+ int dateColumnIndex = 0;
+ for (int index = 0; index < columnModel.getColumnCount(); index++) {
+ //get indexes for created date column and path column
+ if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.CaseNode_column_metadataFilePath())) {
+ originalPathColumnIndex = index;
+ } else if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.CaseNode_column_createdTime())) {
+ dateColumnIndex = index;
+ }
+ }
+ //Hide path column by default will need to
+ ETableColumn column = (ETableColumn) columnModel.getColumn(originalPathColumnIndex);
+ ((ETableColumnModel) columnModel).setColumnHidden(column, true);
+ outline.setRootVisible(false);
+
+ //Sort on Created date column in descending order by default
+ outline.setColumnSorted(dateColumnIndex, false, 1);
+ if (null == em) {
+ em = new ExplorerManager();
+ }
+ caseTableScrollPane.setViewportView(outlineView);
+ this.setVisible(true);
+ outline.setRowSelectionAllowed(false);
+ }
+
+ /**
+ * Add a listener to changes in case selections in the table
+ *
+ * @param listener the ListSelectionListener to add
+ */
+ void addListSelectionListener(ListSelectionListener listener) {
+ outline.getSelectionModel().addListSelectionListener(listener);
+ }
+
+ String getCasePath() {
+ int[] selectedRows = outline.getSelectedRows();
+ if (selectedRows.length == 1) {
+ try {
+ return ((Node.Property) outline.getModel().getValueAt(outline.convertRowIndexToModel(selectedRows[0]), originalPathColumnIndex)).getValue().toString();
+ } catch (IllegalAccessException | InvocationTargetException ex) {
+ LOGGER.log(Level.SEVERE, "Unable to get case path from table.", ex);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Check if a row could be and is selected.
+ *
+ * @return true if a row is selected, false if no row is selected
+ */
+ boolean isRowSelected() {
+ return outline.getRowSelectionAllowed() && outline.getSelectedRows().length > 0;
+ }
+
+ @NbBundle.Messages({"CaseBrowser.caseListLoading.message=Please Wait..."})
+ /**
+ * Gets the list of cases known to the review mode cases manager and
+ * refreshes the cases table.
+ */
+ void refresh() {
+ if (tableWorker == null || tableWorker.isDone()) {
+ outline.setRowSelectionAllowed(false);
+ //create a new TableWorker to and execute it in a background thread if one is not currently working
+ //set the table to display text informing the user that the list is being retreived and disable case selection
+ EmptyNode emptyNode = new EmptyNode(Bundle.CaseBrowser_caseListLoading_message());
+ em.setRootContext(emptyNode);
+ tableWorker = new LoadCaseMapWorker();
+ tableWorker.execute();
+ }
+
+ }
+
+ /**
+ * 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() {
+
+ caseTableScrollPane = new javax.swing.JScrollPane();
+
+ setMinimumSize(new java.awt.Dimension(0, 5));
+ setPreferredSize(new java.awt.Dimension(5, 5));
+ setLayout(new java.awt.BorderLayout());
+
+ caseTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+ caseTableScrollPane.setMinimumSize(new java.awt.Dimension(0, 5));
+ caseTableScrollPane.setOpaque(false);
+ caseTableScrollPane.setPreferredSize(new java.awt.Dimension(5, 5));
+ add(caseTableScrollPane, java.awt.BorderLayout.CENTER);
+ }// //GEN-END:initComponents
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JScrollPane caseTableScrollPane;
+ // End of variables declaration//GEN-END:variables
+
+ /**
+ * Swingworker to fetch the updated map of cases and their status in a
+ * background thread
+ */
+ private class LoadCaseMapWorker extends SwingWorker {
+
+ private static final String ALERT_FILE_NAME = "autoingest.alert";
+ private Map cases;
+
+ /**
+ * Gets a list of the cases in the top level case folder
+ *
+ * @return List of cases.
+ *
+ * @throws CoordinationServiceException
+ */
+ private Map getCases() throws CoordinationService.CoordinationServiceException {
+ Map casesMap = new HashMap<>();
+ List nodeList = CoordinationService.getInstance().getNodeList(CoordinationService.CategoryNode.CASES);
+
+ for (String node : nodeList) {
+ Path casePath = Paths.get(node);
+ File caseFolder = casePath.toFile();
+ if (caseFolder.exists()) {
+ /*
+ * Search for '*.aut' and 'autoingest.alert' files.
+ */
+ File[] fileArray = caseFolder.listFiles();
+ if (fileArray == null) {
+ continue;
+ }
+ String autFilePath = null;
+ boolean alertFileFound = false;
+ for (File file : fileArray) {
+ String name = file.getName().toLowerCase();
+ if (autFilePath == null && name.endsWith(".aut")) {
+ autFilePath = file.getAbsolutePath();
+ if (!alertFileFound) {
+ continue;
+ }
+ }
+ if (!alertFileFound && name.endsWith(ALERT_FILE_NAME)) {
+ alertFileFound = true;
+ }
+ if (autFilePath != null && alertFileFound) {
+ break;
+ }
+ }
+
+ if (autFilePath != null) {
+ try {
+ boolean hasAlertStatus = false;
+ if (alertFileFound) {
+ /*
+ * When an alert file exists, ignore the node
+ * data and use the ALERT status.
+ */
+ hasAlertStatus = true;
+ } else {
+ byte[] rawData = CoordinationService.getInstance().getNodeData(CoordinationService.CategoryNode.CASES, node);
+ if (rawData != null && rawData.length > 0) {
+ /*
+ * When node data exists, use the status
+ * stored in the node data.
+ */
+ CaseNodeData caseNodeData = new CaseNodeData(rawData);
+ if (caseNodeData.getErrorsOccurred()) {
+ hasAlertStatus = true;
+ }
+ }
+ }
+
+ CaseMetadata caseMetadata = new CaseMetadata(Paths.get(autFilePath));
+ casesMap.put(caseMetadata, hasAlertStatus);
+ } catch (CaseMetadata.CaseMetadataException ex) {
+ LOGGER.log(Level.SEVERE, String.format("Error reading case metadata file '%s'.", autFilePath), ex);
+ } catch (InterruptedException | CaseNodeData.InvalidDataException ex) {
+ LOGGER.log(Level.SEVERE, String.format("Error reading case node data for '%s'.", node), ex);
+ }
+ }
+ }
+ }
+ return casesMap;
+ }
+
+ @Override
+ protected Void doInBackground() throws Exception {
+
+ try {
+ cases = getCases();
+ } catch (CoordinationService.CoordinationServiceException ex) {
+ LOGGER.log(Level.SEVERE, "Unexpected exception while refreshing the table.", ex); //NON-NLS
+ }
+ return null;
+ }
+
+ @Override
+ protected void done() {
+ EventQueue.invokeLater(() -> {
+ MultiUserNode caseListNode = new MultiUserNode(cases);
+ em.setRootContext(caseListNode);
+ outline.setRowSelectionAllowed(true);
+ });
+ }
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseManager.java
deleted file mode 100644
index bedc09d799..0000000000
--- a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseManager.java
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
- * Autopsy Forensic Browser
- *
- * Copyright 2011-2017 Basis Technology Corp.
- * Contact: carrier sleuthkit org
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.sleuthkit.autopsy.casemodule;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.List;
-import java.util.Objects;
-import java.util.logging.Level;
-import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
-import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
-import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
-import org.sleuthkit.autopsy.coreutils.Logger;
-
-/**
- * Handles locating and opening multi-user cases.
- */
-final class MultiUserCaseManager {
-
- private static final Logger LOGGER = Logger.getLogger(MultiUserCaseManager.class.getName());
- private static final String ALERT_FILE_NAME = "autoingest.alert";
- private static MultiUserCaseManager instance;
- private CoordinationService coordinationService;
-
- /**
- * Gets the multi-user case manager.
- *
- * @return The multi-user case manager singleton.
- *
- * @throws MultiUserCaseManagerException
- */
- synchronized static MultiUserCaseManager getInstance() throws MultiUserCaseManager.MultiUserCaseManagerException {
- if (null == instance) {
- instance = new MultiUserCaseManager();
- }
- return instance;
- }
-
- /**
- * Constructs an object that handles locating and opening multi-user cases.
- *
- * @throws MultiUserCaseManagerException
- */
- private MultiUserCaseManager() throws MultiUserCaseManagerException {
- try {
- coordinationService = CoordinationService.getInstance();
- } catch (CoordinationServiceException ex) {
- throw new MultiUserCaseManager.MultiUserCaseManagerException("Failed to get the coordination service.", ex);
- }
- }
-
- /**
- * Gets a list of the cases in the top level case folder
- *
- * @return List of cases.
- *
- * @throws CoordinationServiceException
- */
- List getCases() throws CoordinationServiceException {
- List cases = new ArrayList<>();
- List nodeList = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES);
- for (String node : nodeList) {
- Path casePath = Paths.get(node);
- File caseFolder = casePath.toFile();
- if (caseFolder.exists()) {
- /*
- * Search for '*.aut' and 'autoingest.alert' files.
- */
- File[] fileArray = caseFolder.listFiles();
- if (fileArray == null) {
- continue;
- }
- String autFilePath = null;
- boolean alertFileFound = false;
- for (File file : fileArray) {
- String name = file.getName().toLowerCase();
- if (autFilePath == null && name.endsWith(".aut")) {
- autFilePath = file.getAbsolutePath();
- if (!alertFileFound) {
- continue;
- }
- }
- if (!alertFileFound && name.endsWith(ALERT_FILE_NAME)) {
- alertFileFound = true;
- }
- if (autFilePath != null && alertFileFound) {
- break;
- }
- }
-
- if (autFilePath != null) {
- try {
- CaseStatus caseStatus;
- if (alertFileFound) {
- /*
- * When an alert file exists, ignore the node data
- * and use the ALERT status.
- */
- caseStatus = CaseStatus.ALERT;
- } else {
- byte[] rawData = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, node);
- if (rawData != null && rawData.length > 0) {
- /*
- * When node data exists, use the status stored
- * in the node data.
- */
- CaseNodeData caseNodeData = new CaseNodeData(rawData);
- if (caseNodeData.getErrorsOccurred()) {
- caseStatus = CaseStatus.ALERT;
- } else {
- caseStatus = CaseStatus.OK;
- }
- } else {
- /*
- * When no node data is available, use the 'OK'
- * status to avoid confusing the end-user.
- */
- caseStatus = CaseStatus.OK;
- }
- }
-
- CaseMetadata caseMetadata = new CaseMetadata(Paths.get(autFilePath));
- cases.add(new MultiUserCase(casePath, caseMetadata, caseStatus));
- } catch (CaseMetadata.CaseMetadataException | MultiUserCase.MultiUserCaseException ex) {
- LOGGER.log(Level.SEVERE, String.format("Error reading case metadata file '%s'.", autFilePath), ex);
- } catch (InterruptedException | CaseNodeData.InvalidDataException ex) {
- LOGGER.log(Level.SEVERE, String.format("Error reading case node data for '%s'.", node), ex);
- }
- }
- }
- }
- return cases;
- }
-
- /**
- * Opens a multi-user case.
- *
- * @param caseMetadataFilePath Path to the case metadata file.
- *
- * @throws CaseActionException
- */
- synchronized void openCase(Path caseMetadataFilePath) throws CaseActionException {
- /*
- * Open the case.
- */
- Case.openAsCurrentCase(caseMetadataFilePath.toString());
- }
-
- /**
- * Exception type thrown when there is an error completing a multi-user case
- * manager operation.
- */
- static final class MultiUserCaseManagerException extends Exception {
-
- private static final long serialVersionUID = 1L;
-
- /**
- * Constructs an instance of the exception type thrown when there is an
- * error completing a multi-user case manager operation.
- *
- * @param message The exception message.
- */
- private MultiUserCaseManagerException(String message) {
- super(message);
- }
-
- /**
- * Constructs an instance of the exception type thrown when there is an
- * error completing a multi-user case manager operation.
- *
- * @param message The exception message.
- * @param cause A Throwable cause for the error.
- */
- private MultiUserCaseManagerException(String message, Throwable cause) {
- super(message, cause);
- }
-
- }
-
- /**
- * A representation of a multi-user case.
- */
- static class MultiUserCase implements Comparable {
-
- private final Path caseDirectoryPath;
- private final String caseDisplayName;
- private final String metadataFileName;
- private final Date createDate;
- private final Date lastAccessedDate;
- private CaseStatus status;
-
- /**
- * Constructs a representation of a multi-user case
- *
- * @param caseDirectoryPath The case directory path.
- * @param caseMetadata The case metadata.
- *
- * @throws MultiUserCaseException If no case metadata (.aut) file is
- * found in the case directory.
- */
- MultiUserCase(Path caseDirectoryPath, CaseMetadata caseMetadata, CaseStatus status) throws MultiUserCaseException {
- this.caseDirectoryPath = caseDirectoryPath;
- caseDisplayName = caseMetadata.getCaseDisplayName();
- metadataFileName = caseMetadata.getFilePath().getFileName().toString();
- this.status = status;
- BasicFileAttributes fileAttrs = null;
- try {
- fileAttrs = Files.readAttributes(Paths.get(caseDirectoryPath.toString(), metadataFileName), BasicFileAttributes.class);
- } catch (IOException ex) {
- LOGGER.log(Level.SEVERE, String.format("Error reading file attributes of case metadata file in %s, will use current time for case createDate/lastModfiedDate", caseDirectoryPath), ex);
- }
- if (null != fileAttrs) {
- createDate = new Date(fileAttrs.creationTime().toMillis());
- lastAccessedDate = new Date(fileAttrs.lastAccessTime().toMillis());
- } else {
- createDate = new Date();
- lastAccessedDate = new Date();
- }
- }
-
- /**
- * Gets the case directory path.
- *
- * @return The case directory path.
- */
- Path getCaseDirectoryPath() {
- return this.caseDirectoryPath;
- }
-
- /**
- * Gets the case display name. This may differ from the name supplied to
- * the directory or metadata file names if a case has been renamed.
- *
- * @return The case display name.
- */
- String getCaseDisplayName() {
- return this.caseDisplayName;
- }
-
- /**
- * Gets the creation date for the case, defined as the create time of
- * the case metadata file.
- *
- * @return The case creation date.
- */
- Date getCreationDate() {
- return this.createDate;
- }
-
- /**
- * Gets the last accessed date for the case, defined as the last
- * accessed time of the case metadata file.
- *
- * @return The last accessed date.
- */
- Date getLastAccessedDate() {
- return this.lastAccessedDate;
- }
-
- /**
- * Gets metadata (.aut) file name.
- *
- * @return The metadata file name.
- */
- String getMetadataFileName() {
- return this.metadataFileName;
- }
-
- /**
- * Gets the status of this case.
- *
- * @return See CaseStatus enum definition.
- */
- CaseStatus getStatus() {
- return status;
- }
-
- /**
- * Gets the case metadata from a case directory path.
- *
- * @param caseDirectoryPath The case directory path.
- *
- * @return Case metadata.
- *
- * @throws CaseMetadata.CaseMetadataException If the CaseMetadata object
- * cannot be constructed.
- * @throws MultiUserCaseException If no case metadata (.aut)
- * file is found in the case
- * directory.
- */
- private CaseMetadata getCaseMetadataFromCaseDirectoryPath(Path caseDirectoryPath) throws CaseMetadata.CaseMetadataException, MultiUserCaseException {
- CaseMetadata caseMetadata = null;
-
- File directory = new File(caseDirectoryPath.toString());
- if (directory.isDirectory()) {
- File autFile = null;
-
- /*
- * Attempt to find an AUT file via a directory scan.
- */
- for (File file : directory.listFiles()) {
- if (file.getName().toLowerCase().endsWith(CaseMetadata.getFileExtension()) && file.isFile()) {
- autFile = file;
- break;
- }
- }
-
- if (autFile == null || !autFile.isFile()) {
- throw new MultiUserCaseException(String.format("No case metadata (.aut) file found in the case directory '%s'.", caseDirectoryPath.toString()));
- }
-
- caseMetadata = new CaseMetadata(Paths.get(autFile.getAbsolutePath()));
- }
-
- return caseMetadata;
- }
-
- /**
- * Indicates whether or not some other object is "equal to" this
- * MultiUserCase object.
- *
- * @param other The other object.
- *
- * @return True or false.
- */
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof MultiUserCase)) {
- return false;
- }
- if (other == this) {
- return true;
- }
- return this.caseDirectoryPath.toString().equals(((MultiUserCase) other).caseDirectoryPath.toString());
- }
-
- /**
- * Returns a hash code value for this MultiUserCase object.
- *
- * @return The has code.
- */
- @Override
- public int hashCode() {
- int hash = 7;
- hash = 71 * hash + Objects.hashCode(this.caseDirectoryPath);
- hash = 71 * hash + Objects.hashCode(this.createDate);
- hash = 71 * hash + Objects.hashCode(this.caseDisplayName);
- return hash;
- }
-
- /**
- * Compares this MultiUserCase object with another MultiUserCase object
- * for order.
- */
- @Override
- public int compareTo(MultiUserCase other) {
- return -this.lastAccessedDate.compareTo(other.getLastAccessedDate());
- }
-
- /**
- * Comparator for a descending order sort on date created.
- */
- static class LastAccessedDateDescendingComparator implements Comparator {
-
- /**
- * Compares two MultiUserCase objects for order based on last
- * accessed date (descending).
- *
- * @param object The first MultiUserCase object
- * @param otherObject The second MultiUserCase object.
- *
- * @return A negative integer, zero, or a positive integer as the
- * first argument is less than, equal to, or greater than
- * the second.
- */
- @Override
- public int compare(MultiUserCase object, MultiUserCase otherObject) {
- return -object.getLastAccessedDate().compareTo(otherObject.getLastAccessedDate());
- }
- }
-
- /**
- * Exception thrown when there is a problem creating a multi-user case.
- */
- final class MultiUserCaseException extends Exception {
-
- private static final long serialVersionUID = 1L;
-
- /**
- * Constructs an exception to throw when there is a problem creating
- * a multi-user case.
- *
- * @param message The exception message.
- */
- private MultiUserCaseException(String message) {
- super(message);
- }
-
- /**
- * Constructs an exception to throw when there is a problem creating
- * a multi-user case.
- *
- * @param message The exception message.
- * @param cause The cause of the exception, if it was an
- * exception.
- */
- private MultiUserCaseException(String message, Throwable cause) {
- super(message, cause);
- }
- }
-
- }
-
- static enum CaseStatus {
- OK,
- ALERT
- }
-
-}
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.form
index 58ef6f79fb..3f897d70c0 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.form
@@ -1,10 +1,6 @@
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java
index e0d63f07d7..a2a2ec13ad 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java
@@ -19,79 +19,29 @@
package org.sleuthkit.autopsy.casemodule;
import java.awt.Cursor;
-import java.awt.Desktop;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
-import java.util.List;
import java.util.logging.Level;
import javax.swing.JDialog;
-import javax.swing.JOptionPane;
+import javax.swing.JPanel;
import javax.swing.SortOrder;
-import javax.swing.SwingWorker;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.table.DefaultTableModel;
-import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;
import org.openide.util.Lookup;
-import org.openide.util.NbBundle;
-import org.sleuthkit.autopsy.casemodule.MultiUserCaseManager.MultiUserCase;
-import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.coreutils.Logger;
-import org.sleuthkit.autopsy.guiutils.LongDateCellRenderer;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
-import org.sleuthkit.autopsy.guiutils.GrayableCellRenderer;
-import org.sleuthkit.autopsy.guiutils.StatusIconCellRenderer;
/**
* A panel that allows a user to open cases created by auto ingest.
*/
-@NbBundle.Messages({"MultiUSerCasesPanel.caseListLoading.message=Retrieving list of cases, please wait..."})
-final class MultiUserCasesPanel extends javax.swing.JPanel {
+final class MultiUserCasesPanel extends JPanel{
- private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(MultiUserCasesPanel.class.getName());
- private static final String LOG_FILE_NAME = "auto_ingest_log.txt";
- private static final int CASE_COL_MIN_WIDTH = 30;
- private static final int CASE_COL_MAX_WIDTH = 2000;
- private static final int CASE_COL_PREFERRED_WIDTH = 300;
- private static final int TIME_COL_MIN_WIDTH = 40;
- private static final int TIME_COL_MAX_WIDTH = 250;
- private static final int TIME_COL_PREFERRED_WIDTH = 160;
- private static final int STATUS_COL_MIN_WIDTH = 55;
- private static final int STATUS_COL_MAX_WIDTH = 250;
- private static final int STATUS_COL_PREFERRED_WIDTH = 60;
- private static final String CASES_POPULATING_MESSAGE = NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUSerCasesPanel.caseListLoading.message");
-
- /*
- * The JTable table model for the cases table presented by this view is
- * defined by the following string, enum, and array.
- *
- * TODO (RC): Consider unifying this stuff in an enum as in
- * AutoIngestDashboard to make it less error prone.
- */
- private static final String CASE_HEADER = NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.CaseHeaderText");
- private static final String CREATEDTIME_HEADER = NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.CreatedTimeHeaderText");
- private static final String STATUS_ICON_HEADER = NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.StatusIconHeaderText");
- private static final String OUTPUT_FOLDER_HEADER = NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.OutputFolderHeaderText");
- private static final String METADATA_FILE_HEADER = NbBundle.getMessage(MultiUserCasesPanel.class, "ReviewModeCasePanel.MetadataFileHeaderText");
-
- enum COLUMN_HEADERS {
-
- CASE,
- CREATEDTIME,
- STATUS_ICON,
- OUTPUTFOLDER,
- METADATA_FILE
- }
- private final String[] columnNames = {CASE_HEADER, CREATEDTIME_HEADER, STATUS_ICON_HEADER, OUTPUT_FOLDER_HEADER, METADATA_FILE_HEADER};
- private DefaultTableModel caseTableModel;
- private JDialog parentDialog;
- private LoadTableWorker tableWorker;
- private Path currentlySelectedCase;
+ private static final long serialVersionUID = 1L;
+ private final JDialog parentDialog;
+ private final CaseBrowser caseBrowserPanel;
/**
* Constructs a panel that allows a user to open cases created by automated
@@ -99,66 +49,19 @@ final class MultiUserCasesPanel extends javax.swing.JPanel {
*/
MultiUserCasesPanel(JDialog parentDialog) {
this.parentDialog = parentDialog;
- caseTableModel = new DefaultTableModel(columnNames, 0) {
- private static final long serialVersionUID = 1L;
-
- @Override
- public boolean isCellEditable(int row, int column) {
- return false;
- }
-
- @Override
- public Class> getColumnClass(int col) {
- if (this.getColumnName(col).equals(CREATEDTIME_HEADER)) {
- return Date.class;
- } else {
- return super.getColumnClass(col);
- }
- }
- };
-
initComponents();
- /*
- * Configure the columns of the cases table.
- */
- TableColumn theColumn;
- theColumn = casesTable.getColumn(CASE_HEADER);
- theColumn.setCellRenderer(new GrayableCellRenderer());
- theColumn.setMinWidth(CASE_COL_MIN_WIDTH);
- theColumn.setMaxWidth(CASE_COL_MAX_WIDTH);
- theColumn.setPreferredWidth(CASE_COL_PREFERRED_WIDTH);
- theColumn.setWidth(CASE_COL_PREFERRED_WIDTH);
-
- theColumn = casesTable.getColumn(CREATEDTIME_HEADER);
- theColumn.setCellRenderer(new LongDateCellRenderer());
- theColumn.setMinWidth(TIME_COL_MIN_WIDTH);
- theColumn.setMaxWidth(TIME_COL_MAX_WIDTH);
- theColumn.setPreferredWidth(TIME_COL_PREFERRED_WIDTH);
- theColumn.setWidth(TIME_COL_PREFERRED_WIDTH);
-
- theColumn = casesTable.getColumn(STATUS_ICON_HEADER);
- theColumn.setCellRenderer(new StatusIconCellRenderer());
- theColumn.setMinWidth(STATUS_COL_MIN_WIDTH);
- theColumn.setMaxWidth(STATUS_COL_MAX_WIDTH);
- theColumn.setPreferredWidth(STATUS_COL_PREFERRED_WIDTH);
- theColumn.setWidth(STATUS_COL_PREFERRED_WIDTH);
-
- casesTable.removeColumn(casesTable.getColumn(OUTPUT_FOLDER_HEADER));
- casesTable.removeColumn(casesTable.getColumn(METADATA_FILE_HEADER));
- casesTable.setRowSorter(new RowSorter<>(caseTableModel));
- casesTable.getRowSorter().toggleSortOrder(casesTable.getColumn(CREATEDTIME_HEADER).getModelIndex());
+ caseBrowserPanel = new CaseBrowser();
+ caseExplorerScrollPane.add(caseBrowserPanel);
+ caseExplorerScrollPane.setViewportView(caseBrowserPanel);
/*
* Listen for row selection changes and set button state for the current
* selection.
*/
- casesTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> {
- //Ignore extra messages.
- if (e.getValueIsAdjusting()) {
- return;
- }
+ caseBrowserPanel.addListSelectionListener((ListSelectionEvent e) -> {
setButtons();
});
+
}
/**
@@ -166,87 +69,15 @@ final class MultiUserCasesPanel extends javax.swing.JPanel {
* refreshes the cases table.
*/
void refresh() {
- if (tableWorker == null || tableWorker.isDone()) {
- //create a new TableWorker to and execute it in a background thread if one is not currently working
- currentlySelectedCase = getSelectedCase();
- //set the table to display text informing the user that the list is being retreived and disable case selection
- caseTableModel.setRowCount(0);
- casesTable.setRowSelectionAllowed(false);
- caseTableModel.addRow(new Object[]{CASES_POPULATING_MESSAGE, null, null, "", ""});
- tableWorker = new LoadTableWorker();
- tableWorker.execute();
- }
- }
-
- /**
- * Gets the current selection in the cases table.
- *
- * @return A path representing the current selected case, null if there is
- * no selection.
- */
- private Path getSelectedCase() {
- try {
- int selectedRow = casesTable.getSelectedRow();
- if (selectedRow >= 0 && selectedRow < casesTable.getRowCount()) {
- return Paths.get(caseTableModel.getValueAt(casesTable.convertRowIndexToModel(selectedRow), COLUMN_HEADERS.CASE.ordinal()).toString());
- }
- } catch (Exception ignored) {
- return null;
- }
- return null;
- }
-
- /**
- * Sets the current selection in the cases table.
- *
- * @param path The case folder path of the case to select.
- */
- private void setSelectedCase(Path path) {
- if (path != null) {
- try {
- for (int row = 0; row < casesTable.getRowCount(); ++row) {
- Path temp = Paths.get(caseTableModel.getValueAt(casesTable.convertRowIndexToModel(row), COLUMN_HEADERS.CASE.ordinal()).toString());
- if (temp.compareTo(path) == 0) { // found it
- casesTable.setRowSelectionInterval(row, row);
- return;
- }
- }
- } catch (Exception ignored) {
- casesTable.clearSelection();
- }
- }
- casesTable.clearSelection();
+ caseBrowserPanel.refresh();
}
/**
* Enables/disables the Open and Show Log buttons based on the case selected
* in the cases table.
*/
- private void setButtons() {
- boolean openEnabled = casesTable.getRowSelectionAllowed() && casesTable.getSelectedRow() >= 0 && casesTable.getSelectedRow() < casesTable.getRowCount();
- bnOpen.setEnabled(openEnabled);
-
- Path pathToLog = getSelectedCaseLogFilePath();
- boolean showLogEnabled = openEnabled && pathToLog != null && pathToLog.toFile().exists();
- bnShowLog.setEnabled(showLogEnabled);
- }
-
- /**
- * Retrieves the log file path for the selected case in the cases table.
- *
- * @return The case log path.
- */
- private Path getSelectedCaseLogFilePath() {
- Path retValue = null;
-
- int selectedRow = casesTable.getSelectedRow();
- int rowCount = casesTable.getRowCount();
- if (selectedRow >= 0 && selectedRow < rowCount) {
- String caseDirectory = (String) caseTableModel.getValueAt(casesTable.convertRowIndexToModel(selectedRow), COLUMN_HEADERS.OUTPUTFOLDER.ordinal());
- retValue = Paths.get(caseDirectory, LOG_FILE_NAME);
- }
-
- return retValue;
+ void setButtons() {
+ bnOpen.setEnabled(caseBrowserPanel.isRowSelected());
}
/**
@@ -254,56 +85,34 @@ final class MultiUserCasesPanel extends javax.swing.JPanel {
*
* @param caseMetadataFilePath The path to the case metadata file.
*/
- private void openCase(Path caseMetadataFilePath) {
- setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ private void openCase(String caseMetadataFilePath) {
+ if (caseMetadataFilePath != null) {
+ setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
- StartupWindowProvider.getInstance().close();
- if (parentDialog != null) {
- parentDialog.setVisible(false);
- }
- new Thread(() -> {
- try {
- MultiUserCaseManager.getInstance().openCase(caseMetadataFilePath);
- } catch (CaseActionException | MultiUserCaseManager.MultiUserCaseManagerException ex) {
- if (null != ex.getCause() && !(ex.getCause() instanceof CaseActionCancelledException)) {
- LOGGER.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", caseMetadataFilePath), ex); //NON-NLS
- MessageNotifyUtil.Message.error(ex.getCause().getLocalizedMessage());
- }
- SwingUtilities.invokeLater(() -> {
- //GUI changes done back on the EDT
- StartupWindowProvider.getInstance().open();
- });
- } finally {
- SwingUtilities.invokeLater(() -> {
- //GUI changes done back on the EDT
- setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
- });
+ StartupWindowProvider.getInstance().close();
+ if (parentDialog != null) {
+ parentDialog.setVisible(false);
}
- }).start();
- }
-
- /**
- * Indicates whether or not a time satisfies a time filter defined by this
- * panel's time filter radio buttons.
- *
- * @param currentTime The current date and time in milliseconds from the
- * Unix epoch.
- * @param inputTime The date and time to be tested as milliseconds from
- * the Unix epoch.
- */
- private boolean passesTimeFilter(long currentTime, long inputTime) {
- long numberOfUnits = 10;
- long multiplier = 1;
- if (rbAllCases.isSelected()) {
- return true;
- } else if (rbMonths.isSelected()) {
- multiplier = 31;
- } else if (rbWeeks.isSelected()) {
- multiplier = 7;
- } else if (rbDays.isSelected()) {
- multiplier = 1;
+ new Thread(() -> {
+ try {
+ Case.openAsCurrentCase(caseMetadataFilePath);
+ } catch (CaseActionException ex) {
+ if (null != ex.getCause() && !(ex.getCause() instanceof CaseActionCancelledException)) {
+ LOGGER.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", caseMetadataFilePath), ex); //NON-NLS
+ MessageNotifyUtil.Message.error(ex.getCause().getLocalizedMessage());
+ }
+ SwingUtilities.invokeLater(() -> {
+ //GUI changes done back on the EDT
+ StartupWindowProvider.getInstance().open();
+ });
+ } finally {
+ SwingUtilities.invokeLater(() -> {
+ //GUI changes done back on the EDT
+ setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ });
+ }
+ }).start();
}
- return ((currentTime - inputTime) / (1000 * 60 * 60 * 24)) < (numberOfUnits * multiplier);
}
/**
@@ -345,19 +154,11 @@ final class MultiUserCasesPanel extends javax.swing.JPanel {
// //GEN-BEGIN:initComponents
private void initComponents() {
- rbGroupHistoryLength = new javax.swing.ButtonGroup();
bnOpen = new javax.swing.JButton();
- scrollPaneTable = new javax.swing.JScrollPane();
- casesTable = new javax.swing.JTable();
- bnRefresh = new javax.swing.JButton();
- panelFilter = new javax.swing.JPanel();
- rbAllCases = new javax.swing.JRadioButton();
- bnShowLog = new javax.swing.JButton();
- rbDays = new javax.swing.JRadioButton();
- rbWeeks = new javax.swing.JRadioButton();
- rbMonths = new javax.swing.JRadioButton();
- rbGroupLabel = new javax.swing.JLabel();
bnOpenSingleUserCase = new javax.swing.JButton();
+ cancelButton = new javax.swing.JButton();
+ searchLabel = new javax.swing.JLabel();
+ caseExplorerScrollPane = new javax.swing.JScrollPane();
setName("Completed Cases"); // NOI18N
setPreferredSize(new java.awt.Dimension(960, 485));
@@ -370,84 +171,6 @@ final class MultiUserCasesPanel extends javax.swing.JPanel {
}
});
- casesTable.setModel(caseTableModel);
- casesTable.setRowHeight(20);
- casesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
- casesTable.addMouseListener(new java.awt.event.MouseAdapter() {
- public void mouseClicked(java.awt.event.MouseEvent evt) {
- casesTableMouseClicked(evt);
- }
- });
- scrollPaneTable.setViewportView(casesTable);
-
- org.openide.awt.Mnemonics.setLocalizedText(bnRefresh, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnRefresh.text")); // NOI18N
- bnRefresh.addActionListener(new java.awt.event.ActionListener() {
- public void actionPerformed(java.awt.event.ActionEvent evt) {
- bnRefreshActionPerformed(evt);
- }
- });
-
- rbGroupHistoryLength.add(rbAllCases);
- rbAllCases.setSelected(true);
- org.openide.awt.Mnemonics.setLocalizedText(rbAllCases, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.rbAllCases.text")); // NOI18N
- rbAllCases.addItemListener(new java.awt.event.ItemListener() {
- public void itemStateChanged(java.awt.event.ItemEvent evt) {
- rbAllCasesItemStateChanged(evt);
- }
- });
-
- javax.swing.GroupLayout panelFilterLayout = new javax.swing.GroupLayout(panelFilter);
- panelFilter.setLayout(panelFilterLayout);
- panelFilterLayout.setHorizontalGroup(
- panelFilterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(panelFilterLayout.createSequentialGroup()
- .addComponent(rbAllCases)
- .addGap(0, 0, Short.MAX_VALUE))
- );
- panelFilterLayout.setVerticalGroup(
- panelFilterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelFilterLayout.createSequentialGroup()
- .addGap(0, 0, 0)
- .addComponent(rbAllCases))
- );
-
- org.openide.awt.Mnemonics.setLocalizedText(bnShowLog, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnShowLog.text")); // NOI18N
- bnShowLog.setToolTipText(org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnShowLog.toolTipText")); // NOI18N
- bnShowLog.setEnabled(false);
- bnShowLog.addActionListener(new java.awt.event.ActionListener() {
- public void actionPerformed(java.awt.event.ActionEvent evt) {
- bnShowLogActionPerformed(evt);
- }
- });
-
- rbGroupHistoryLength.add(rbDays);
- org.openide.awt.Mnemonics.setLocalizedText(rbDays, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.rbDays.text")); // NOI18N
- rbDays.setName(""); // NOI18N
- rbDays.addItemListener(new java.awt.event.ItemListener() {
- public void itemStateChanged(java.awt.event.ItemEvent evt) {
- rbDaysItemStateChanged(evt);
- }
- });
-
- rbGroupHistoryLength.add(rbWeeks);
- org.openide.awt.Mnemonics.setLocalizedText(rbWeeks, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.rbWeeks.text")); // NOI18N
- rbWeeks.addItemListener(new java.awt.event.ItemListener() {
- public void itemStateChanged(java.awt.event.ItemEvent evt) {
- rbWeeksItemStateChanged(evt);
- }
- });
-
- rbGroupHistoryLength.add(rbMonths);
- org.openide.awt.Mnemonics.setLocalizedText(rbMonths, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.rbMonths.text")); // NOI18N
- rbMonths.addItemListener(new java.awt.event.ItemListener() {
- public void itemStateChanged(java.awt.event.ItemEvent evt) {
- rbMonthsItemStateChanged(evt);
- }
- });
-
- rbGroupLabel.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
- org.openide.awt.Mnemonics.setLocalizedText(rbGroupLabel, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.rbGroupLabel.text")); // NOI18N
-
org.openide.awt.Mnemonics.setLocalizedText(bnOpenSingleUserCase, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnOpenSingleUserCase.text")); // NOI18N
bnOpenSingleUserCase.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
@@ -455,55 +178,45 @@ final class MultiUserCasesPanel extends javax.swing.JPanel {
}
});
+ org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.cancelButton.text")); // NOI18N
+ cancelButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ cancelButtonActionPerformed(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(searchLabel, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.searchLabel.text")); // NOI18N
+
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(layout.createSequentialGroup()
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(caseExplorerScrollPane)
.addGroup(layout.createSequentialGroup()
- .addGap(4, 4, 4)
+ .addComponent(searchLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 555, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 175, Short.MAX_VALUE)
.addComponent(bnOpen, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(bnOpenSingleUserCase)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(bnShowLog)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 13, Short.MAX_VALUE)
- .addComponent(rbGroupLabel)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(rbDays)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(rbWeeks)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(rbMonths)
- .addGap(0, 0, 0)
- .addComponent(panelFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addGap(14, 14, 14)
- .addComponent(bnRefresh))
- .addComponent(scrollPaneTable))
+ .addComponent(cancelButton)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(6, 6, 6)
- .addComponent(scrollPaneTable, javax.swing.GroupLayout.PREFERRED_SIZE, 450, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(caseExplorerScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 450, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
- .addComponent(bnOpen)
- .addComponent(bnOpenSingleUserCase)
- .addComponent(bnShowLog))
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
- .addComponent(rbDays)
- .addComponent(rbWeeks)
- .addComponent(rbMonths)
- .addComponent(rbGroupLabel))
- .addComponent(panelFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addComponent(bnRefresh, javax.swing.GroupLayout.Alignment.TRAILING))
- .addGap(0, 0, 0))
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(cancelButton)
+ .addComponent(bnOpen)
+ .addComponent(bnOpenSingleUserCase)
+ .addComponent(searchLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
}// //GEN-END:initComponents
@@ -513,129 +226,24 @@ final class MultiUserCasesPanel extends javax.swing.JPanel {
* @param evt -- The event that caused this to be called
*/
private void bnOpenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOpenActionPerformed
- int modelRow = casesTable.convertRowIndexToModel(casesTable.getSelectedRow());
- String caseDirectory = (String) caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.OUTPUTFOLDER.ordinal());
- Path caseMetadataFilePath = Paths.get(caseDirectory, (String) caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.METADATA_FILE.ordinal()));
- openCase(caseMetadataFilePath);
+ openCase(caseBrowserPanel.getCasePath());
}//GEN-LAST:event_bnOpenActionPerformed
- /**
- * Refresh button action
- *
- * @param evt -- The event that caused this to be called
- */
- private void bnRefreshActionPerformed(java.awt.event.ActionEvent evt) {
- refresh();
- }
-
- private void rbDaysItemStateChanged(java.awt.event.ItemEvent evt) {
- if (rbDays.isSelected()) {
- refresh();
- }
- }
-
- private void rbAllCasesItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_rbAllCasesItemStateChanged
- if (rbAllCases.isSelected()) {
- refresh();
- }
- }//GEN-LAST:event_rbAllCasesItemStateChanged
-
- private void rbMonthsItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_rbMonthsItemStateChanged
- if (rbMonths.isSelected()) {
- refresh();
- }
- }//GEN-LAST:event_rbMonthsItemStateChanged
-
- private void rbWeeksItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_rbWeeksItemStateChanged
- if (rbWeeks.isSelected()) {
- refresh();
- }
- }//GEN-LAST:event_rbWeeksItemStateChanged
-
- private void bnShowLogActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnShowLogActionPerformed
- Path pathToLog = getSelectedCaseLogFilePath();
- if (pathToLog != null) {
- try {
- if (pathToLog.toFile().exists()) {
- Desktop.getDesktop().edit(pathToLog.toFile());
-
- } else {
- JOptionPane.showMessageDialog(this, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "DisplayLogDialog.cannotFindLog"),
- org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "DisplayLogDialog.unableToShowLogFile"), JOptionPane.ERROR_MESSAGE);
- }
- } catch (IOException ex) {
- LOGGER.log(Level.SEVERE, String.format("Error attempting to open case auto ingest log file %s", pathToLog), ex);
- JOptionPane.showMessageDialog(this,
- org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "DisplayLogDialog.cannotOpenLog"),
- org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "DisplayLogDialog.unableToShowLogFile"),
- JOptionPane.PLAIN_MESSAGE);
- }
- }
- }//GEN-LAST:event_bnShowLogActionPerformed
-
- private void casesTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_casesTableMouseClicked
- if (evt.getClickCount() == 2 && casesTable.getRowSelectionAllowed() && casesTable.getSelectedRow() >= 0 && casesTable.getSelectedRow() < casesTable.getRowCount()) {
- int modelRow = casesTable.convertRowIndexToModel(casesTable.getSelectedRow());
- String caseDirectory = (String) caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.OUTPUTFOLDER.ordinal());
- Path caseMetadataFilePath = Paths.get(caseDirectory, (String) caseTableModel.getValueAt(modelRow, COLUMN_HEADERS.METADATA_FILE.ordinal()));
- openCase(caseMetadataFilePath);
- }
- }//GEN-LAST:event_casesTableMouseClicked
-
private void bnOpenSingleUserCaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOpenSingleUserCaseActionPerformed
Lookup.getDefault().lookup(CaseOpenAction.class).openCaseSelectionWindow();
}//GEN-LAST:event_bnOpenSingleUserCaseActionPerformed
+ private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
+ if (parentDialog != null) {
+ parentDialog.setVisible(false);
+ }
+ }//GEN-LAST:event_cancelButtonActionPerformed
+
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton bnOpen;
private javax.swing.JButton bnOpenSingleUserCase;
- private javax.swing.JButton bnRefresh;
- private javax.swing.JButton bnShowLog;
- private javax.swing.JTable casesTable;
- private javax.swing.JPanel panelFilter;
- private javax.swing.JRadioButton rbAllCases;
- private javax.swing.JRadioButton rbDays;
- private javax.swing.ButtonGroup rbGroupHistoryLength;
- private javax.swing.JLabel rbGroupLabel;
- private javax.swing.JRadioButton rbMonths;
- private javax.swing.JRadioButton rbWeeks;
- private javax.swing.JScrollPane scrollPaneTable;
+ private javax.swing.JButton cancelButton;
+ private javax.swing.JScrollPane caseExplorerScrollPane;
+ private javax.swing.JLabel searchLabel;
// End of variables declaration//GEN-END:variables
-
- private class LoadTableWorker extends SwingWorker {
-
- private List cases;
-
- @Override
- protected Void doInBackground() throws Exception {
-
- try {
- MultiUserCaseManager manager = MultiUserCaseManager.getInstance();
- cases = manager.getCases();
- } catch (MultiUserCaseManager.MultiUserCaseManagerException | CoordinationService.CoordinationServiceException ex) {
- LOGGER.log(Level.SEVERE, "Unexpected exception while refreshing the table.", ex); //NON-NLS
- }
- return null;
- }
-
- @Override
- protected void done() {
- caseTableModel.setRowCount(0);
- long now = new Date().getTime();
- for (MultiUserCase autoIngestCase : cases) {
- if (autoIngestCase.getCreationDate() != null && passesTimeFilter(now, autoIngestCase.getCreationDate().getTime())) {
- caseTableModel.addRow(new Object[]{
- autoIngestCase.getCaseDisplayName(),
- autoIngestCase.getCreationDate(),
- (MultiUserCaseManager.CaseStatus.OK != autoIngestCase.getStatus()) ? StatusIconCellRenderer.Status.WARNING : StatusIconCellRenderer.Status.OK,
- autoIngestCase.getCaseDirectoryPath().toString(),
- autoIngestCase.getMetadataFileName()});
- }
- }
- //ensure the cases are able to be selected
- casesTable.setRowSelectionAllowed(true);
- setSelectedCase(currentlySelectedCase);
- setButtons();
- }
- }
}
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserNode.java b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserNode.java
new file mode 100644
index 0000000000..9443ae35e9
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserNode.java
@@ -0,0 +1,241 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2017-2018 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.casemodule;
+
+import java.awt.Desktop;
+import java.awt.event.ActionEvent;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+import org.openide.nodes.AbstractNode;
+import org.openide.nodes.ChildFactory;
+import org.openide.nodes.Children;
+import org.openide.nodes.Node;
+import org.openide.nodes.Sheet;
+import org.openide.util.NbBundle.Messages;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
+import org.sleuthkit.autopsy.datamodel.NodeProperty;
+
+/**
+ * A root node containing child nodes of the multi user cases
+ */
+final class MultiUserNode extends AbstractNode {
+
+ @Messages({"CaseNode.column.name=Name",
+ "CaseNode.column.createdTime=Created Time",
+ "CaseNode.column.status=Status",
+ "CaseNode.column.metadataFilePath=Path"})
+ private static final Logger LOGGER = Logger.getLogger(MultiUserNode.class.getName());
+ private static final String LOG_FILE_NAME = "auto_ingest_log.txt";
+
+ /**
+ * Provides a root node with children which each represent a case.
+ *
+ * @param caseMap the map of cases and a boolean indicating if they have an
+ * alert
+ */
+ MultiUserNode(Map caseMap) {
+ super(Children.create(new MultiUserNodeChildren(caseMap), true));
+ }
+
+ static class MultiUserNodeChildren extends ChildFactory> {
+
+ private final Map caseMap;
+
+ MultiUserNodeChildren(Map caseMap) {
+ this.caseMap = caseMap;
+ }
+
+ @Override
+ protected boolean createKeys(List> list) {
+ if (caseMap != null && caseMap.size() > 0) {
+ list.addAll(caseMap.entrySet());
+ }
+ return true;
+ }
+
+ @Override
+ protected Node createNodeForKey(Entry key) {
+ return new MultiUserCaseNode(key);
+ }
+
+ }
+
+ /**
+ * A node which represents a single multi user case.
+ */
+ static final class MultiUserCaseNode extends AbstractNode {
+
+ private final String caseName;
+ private final String caseCreatedDate;
+ private final String caseMetadataFilePath;
+ private final boolean caseHasAlert;
+ private final Path caseLogFilePath;
+
+ MultiUserCaseNode(Entry multiUserCase) {
+ super(Children.LEAF);
+ caseName = multiUserCase.getKey().getCaseDisplayName();
+ caseCreatedDate = multiUserCase.getKey().getCreatedDate();
+ caseHasAlert = multiUserCase.getValue();
+ super.setName(caseName);
+ setName(caseName);
+ setDisplayName(caseName);
+ caseMetadataFilePath = multiUserCase.getKey().getFilePath().toString();
+ caseLogFilePath = Paths.get(multiUserCase.getKey().getCaseDirectory(), LOG_FILE_NAME);
+ }
+
+ /**
+ * Returns action to open the Case represented by this node
+ * @return an action which will open the current case
+ */
+ @Override
+ public Action getPreferredAction() {
+ return new OpenMultiUserCaseAction(caseMetadataFilePath);
+ }
+
+ @Messages({"MultiUserNode.AlertColumn.text=Alert"}) //text to display when there is an alert present
+ @Override
+ protected Sheet createSheet() {
+ Sheet s = super.createSheet();
+ Sheet.Set ss = s.get(Sheet.PROPERTIES);
+ if (ss == null) {
+ ss = Sheet.createPropertiesSet();
+ s.put(ss);
+ }
+ ss.put(new NodeProperty<>(Bundle.CaseNode_column_name(), Bundle.CaseNode_column_name(), Bundle.CaseNode_column_name(),
+ caseName));
+ ss.put(new NodeProperty<>(Bundle.CaseNode_column_createdTime(), Bundle.CaseNode_column_createdTime(), Bundle.CaseNode_column_createdTime(),
+ caseCreatedDate));
+ ss.put(new NodeProperty<>(Bundle.CaseNode_column_status(), Bundle.CaseNode_column_status(), Bundle.CaseNode_column_status(),
+ (caseHasAlert == true ? Bundle.MultiUserNode_AlertColumn_text() : "")));
+ ss.put(new NodeProperty<>(Bundle.CaseNode_column_metadataFilePath(), Bundle.CaseNode_column_metadataFilePath(), Bundle.CaseNode_column_metadataFilePath(),
+ caseMetadataFilePath));
+ return s;
+ }
+
+ @Override
+ public Action[] getActions(boolean context) {
+ List actions = new ArrayList<>();
+ actions.add(new OpenMultiUserCaseAction(caseMetadataFilePath)); //open case context menu option
+ actions.add(new OpenCaseLogAction(caseLogFilePath));
+ return actions.toArray(new Action[actions.size()]);
+ }
+ }
+
+ @Messages({"MultiUserNode.OpenMultiUserCaseAction.text=Open Case"})
+ /**
+ * An action that opens the specified case and hides the multi user case
+ * panel.
+ */
+ private static final class OpenMultiUserCaseAction extends AbstractAction {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String caseMetadataFilePath;
+
+ OpenMultiUserCaseAction(String path) {
+ super(Bundle.MultiUserNode_OpenMultiUserCaseAction_text());
+ caseMetadataFilePath = path;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ StartupWindowProvider.getInstance().close();
+ MultiUserCasesDialog.getInstance().setVisible(false);
+ new Thread(
+ () -> {
+ try {
+ Case.openAsCurrentCase(caseMetadataFilePath);
+ } catch (CaseActionException ex) {
+ if (null != ex.getCause() && !(ex.getCause() instanceof CaseActionCancelledException)) {
+ LOGGER.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", caseMetadataFilePath), ex); //NON-NLS
+ MessageNotifyUtil.Message.error(ex.getCause().getLocalizedMessage());
+ }
+ SwingUtilities.invokeLater(() -> {
+ //GUI changes done back on the EDT
+ StartupWindowProvider.getInstance().open();
+ MultiUserCasesDialog.getInstance().setVisible(true);
+ });
+ }
+ }
+ ).start();
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone(); //To change body of generated methods, choose Tools | Templates.
+ }
+ }
+
+ @Messages({"MultiUserNode.OpenCaseLogAction.text=Open Log File"})
+ /**
+ * An action that opens the specified case and hides the multi user case
+ * panel.
+ */
+ private static final class OpenCaseLogAction extends AbstractAction {
+
+ private static final long serialVersionUID = 1L;
+
+ private final Path pathToLog;
+
+ OpenCaseLogAction(Path caseLogFilePath) {
+ super(Bundle.MultiUserNode_OpenCaseLogAction_text());
+ pathToLog = caseLogFilePath;
+ this.setEnabled(caseLogFilePath != null && caseLogFilePath.toFile().exists());
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ if (pathToLog != null) {
+ try {
+ if (pathToLog.toFile().exists()) {
+ Desktop.getDesktop().edit(pathToLog.toFile());
+
+ } else {
+ JOptionPane.showMessageDialog(MultiUserCasesDialog.getInstance(), org.openide.util.NbBundle.getMessage(MultiUserNode.class, "DisplayLogDialog.cannotFindLog"),
+ org.openide.util.NbBundle.getMessage(MultiUserNode.class, "DisplayLogDialog.unableToShowLogFile"), JOptionPane.ERROR_MESSAGE);
+ }
+ } catch (IOException ex) {
+ LOGGER.log(Level.SEVERE, String.format("Error attempting to open case auto ingest log file %s", pathToLog), ex);
+ JOptionPane.showMessageDialog(MultiUserCasesDialog.getInstance(),
+ org.openide.util.NbBundle.getMessage(MultiUserNode.class, "DisplayLogDialog.cannotOpenLog"),
+ org.openide.util.NbBundle.getMessage(MultiUserNode.class, "DisplayLogDialog.unableToShowLogFile"),
+ JOptionPane.PLAIN_MESSAGE);
+ }
+ }
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone(); //To change body of generated methods, choose Tools | Templates.
+ }
+ }
+
+}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java
index e6e8afed52..2713992139 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java
@@ -41,7 +41,7 @@ public interface DisplayableItemNodeVisitor {
T visit(LocalFileNode dfn);
T visit(VirtualDirectoryNode ldn);
-
+
T visit(LocalDirectoryNode ldn);
T visit(DirectoryNode dn);
diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddRawImageTask.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddRawImageTask.java
index 14a3dc9e2f..3d576eebf0 100644
--- a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddRawImageTask.java
+++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddRawImageTask.java
@@ -178,7 +178,7 @@ final class AddRawImageTask implements Runnable {
logger.log(Level.SEVERE, errorMessage, ex);
criticalErrorOccurred = true;
} finally {
- caseDatabase.releaseSingleUserCaseReadLock();
+ caseDatabase.releaseSingleUserCaseWriteLock();
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java
index 4a3a20da07..7fca6a896c 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java
@@ -51,6 +51,7 @@ import org.apache.tika.mime.MimeTypeException;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.Parser;
+import org.apache.tika.parser.microsoft.OfficeParserConfig;
import org.apache.tika.sax.BodyContentHandler;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
@@ -231,11 +232,13 @@ class MSOfficeEmbeddedContentExtractor {
// write limit (which defaults to 100,000 characters.
ContentHandler contentHandler = new BodyContentHandler(-1);
- // TODO: this will be needed once we upgrade to Tika 1.16 or later.
- // OfficeParserConfig officeParserConfig = new OfficeParserConfig();
- // officeParserConfig.setUseSAXPptxExtractor(true);
- // officeParserConfig.setUseSAXDocxExtractor(true);
- // parseContext.set(OfficeParserConfig.class, officeParserConfig);
+ // Use the more memory efficient Tika SAX parsers for DOCX and
+ // PPTX files (it already uses SAX for XLSX).
+ OfficeParserConfig officeParserConfig = new OfficeParserConfig();
+ officeParserConfig.setUseSAXPptxExtractor(true);
+ officeParserConfig.setUseSAXDocxExtractor(true);
+ parseContext.set(OfficeParserConfig.class, officeParserConfig);
+
EmbeddedDocumentExtractor extractor = new EmbeddedContentExtractor(parseContext);
parseContext.set(EmbeddedDocumentExtractor.class, extractor);
ReadContentInputStream stream = new ReadContentInputStream(abstractFile);
diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModule.java b/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModule.java
index 899de3630c..9d08bf7115 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModule.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModule.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2013 Basis Technology Corp.
+ * Copyright 2013 - 2018 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,7 +29,7 @@ import java.util.Map;
import java.util.logging.Level;
import javax.swing.JPanel;
import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBException;
+import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import org.mitre.cybox.cybox_2.ObjectType;
@@ -100,7 +100,7 @@ public class STIXReportModule implements GeneralReportModule {
// Start the progress bar and setup the report
progressPanel.setIndeterminate(false);
progressPanel.start();
- progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "STIXReportModule.progress.readSTIX"));
+ progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "STIXReportModule.progress.readSTIX"));
reportPath = baseReportDir + getRelativeFilePath();
File reportFile = new File(reportPath);
// Check if the user wants to display all output or just hits
diff --git a/Core/src/org/sleuthkit/autopsy/report/Bundle.properties b/Core/src/org/sleuthkit/autopsy/report/Bundle.properties
index 09c6fa8906..2da0862826 100644
--- a/Core/src/org/sleuthkit/autopsy/report/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/report/Bundle.properties
@@ -47,13 +47,13 @@ ReportBodyFile.getDesc.text=Body file format report with MAC times for every fil
ReportBodyFile.getFilePath.text=BodyFile.txt
ReportKML.progress.querying=Querying files...
ReportKML.progress.loading=Loading files...
-ReportKML.getName.text=Google Earth/KML
+ReportKML.getName.text=Google Earth KML
ReportKML.getDesc.text=KML format report with coordinates for relevant files. This format can be used for google earth views.
ReportKML.getFilePath.text=ReportKML.kml
ReportBranding.defaultReportTitle.text=Autopsy Forensic Report
ReportBranding.defaultReportFooter.text=Powered by Autopsy Open Source Digital Forensics Platform - www.sleuthkit.org
ReportExcel.numAartifacts.text=Number of artifacts\:
-ReportExcel.getName.text=Results - Excel
+ReportExcel.getName.text=Excel Report
ReportExcel.getDesc.text=A report about results and tagged items in Excel (XLS) format.
ReportExcel.sheetName.text=Summary
ReportExcel.cellVal.summary=Summary
@@ -177,12 +177,12 @@ ReportGenerator.errors.reportErrorText=Error generating report:
ReportHTML.addThumbRows.dataType.title=Tagged Images - {0}
ReportHTML.addThumbRows.dataType.msg=Tagged Results and Contents that contain images.
ReportHTML.thumbLink.tags=Tags\:
-ReportHTML.getName.text=Results - HTML
+ReportHTML.getName.text=HTML Report
ReportHTML.getDesc.text=A report about results and tagged items in HTML format.
ReportHTML.writeIndex.title=for case {0}
ReportHTML.writeIndex.noFrames.msg=Your browser is not compatible with our frame setup.
-ReportHTML.writeIndex.noFrames.seeNav=Please see the navigation page for artifact links,
-ReportHTML.writeIndex.seeSum=and the summary page for a case summary.
+ReportHTML.writeIndex.noFrames.seeNav=Please see the navigation page for artifact links,
+ReportHTML.writeIndex.seeSum=and the summary page for a case summary.
ReportHTML.writeNav.title=Report Navigation
ReportHTML.writeNav.h1=Report Navigation
ReportHTML.writeNav.summary=Case Summary
@@ -190,7 +190,7 @@ ReportHTML.writeSum.title=Case Summary
ReportHTML.writeSum.warningMsg=Warning, this report was run before ingest services completed\!
#
# autopsy/test/scripts/regression.py._html_report_diff() uses reportGenOn.text, caseName, caseNum,
-# examiner as a regex signature to skip index.html and summary.html
+# examiner as a regex signature to skip report.html and summary.html
#
ReportHTML.writeSum.reportGenOn.text=HTML Report Generated on {0}
ReportHTML.writeSum.caseName=Case\:
diff --git a/Core/src/org/sleuthkit/autopsy/report/FileReportText.java b/Core/src/org/sleuthkit/autopsy/report/FileReportText.java
index 6b22e4a962..c9a239cf0a 100644
--- a/Core/src/org/sleuthkit/autopsy/report/FileReportText.java
+++ b/Core/src/org/sleuthkit/autopsy/report/FileReportText.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2013 Basis Technology Corp.
+ * Copyright 2013 - 2018 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportBodyFile.java b/Core/src/org/sleuthkit/autopsy/report/ReportBodyFile.java
index c495ac2c72..33609f7333 100644
--- a/Core/src/org/sleuthkit/autopsy/report/ReportBodyFile.java
+++ b/Core/src/org/sleuthkit/autopsy/report/ReportBodyFile.java
@@ -2,7 +2,7 @@
*
* Autopsy Forensic Browser
*
- * Copyright 2012-2014 Basis Technology Corp.
+ * Copyright 2012-2018 Basis Technology Corp.
*
* Copyright 2012 42six Solutions.
* Contact: aebadirad 42six com
@@ -76,7 +76,7 @@ class ReportBodyFile implements GeneralReportModule {
progressPanel.setIndeterminate(false);
progressPanel.start();
progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportBodyFile.progress.querying"));
- reportPath = baseReportDir + "BodyFile.txt"; //NON-NLS
+ reportPath = baseReportDir + getRelativeFilePath(); //NON-NLS
currentCase = Case.getCurrentCase();
skCase = currentCase.getSleuthkitCase();
diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportExcel.java b/Core/src/org/sleuthkit/autopsy/report/ReportExcel.java
index 56de46f04d..19526a0a19 100644
--- a/Core/src/org/sleuthkit/autopsy/report/ReportExcel.java
+++ b/Core/src/org/sleuthkit/autopsy/report/ReportExcel.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2013-2017 Basis Technology Corp.
+ * Copyright 2013-2018 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -66,7 +66,7 @@ class ReportExcel implements TableReportModule {
public void startReport(String baseReportDir) {
// Set the path and save it for when the report is written to disk.
this.reportPath = baseReportDir + getRelativeFilePath();
-
+
// Make a workbook.
wb = new XSSFWorkbook();
diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java
index afad0d5746..0c8aa263c3 100644
--- a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java
+++ b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2013 Basis Technology Corp.
+ * Copyright 2013 - 2018 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -63,7 +63,7 @@ class ReportGenerator {
*/
private ReportProgressPanel progressPanel;
- private final String reportPath;
+ private String reportPathFormatString;
private final ReportGenerationPanel reportGenerationPanel = new ReportGenerationPanel();
static final String REPORTS_DIR = "Reports"; //NON-NLS
@@ -93,19 +93,12 @@ class ReportGenerator {
DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss");
Date date = new Date();
String dateNoTime = dateFormat.format(date);
- this.reportPath = currentCase.getReportDirectory() + File.separator + currentCase.getDisplayName() + " " + dateNoTime + File.separator;
+ this.reportPathFormatString = currentCase.getReportDirectory() + File.separator + currentCase.getDisplayName() + " %s " + dateNoTime + File.separator;
this.errorList = new ArrayList<>();
-
- // Create the root reports directory.
- try {
- FileUtil.createFolder(new File(this.reportPath));
- } catch (IOException ex) {
- errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedMakeRptFolder"));
- logger.log(Level.SEVERE, "Failed to make report folder, may be unable to generate reports.", ex); //NON-NLS
- }
}
+
/**
* Display the progress panels to the user, and add actions to close the
* parent dialog.
@@ -145,9 +138,17 @@ class ReportGenerator {
*/
void generateGeneralReport(GeneralReportModule generalReportModule) {
if (generalReportModule != null) {
+ reportPathFormatString = String.format(reportPathFormatString, generalReportModule.getName());
+ // Create the root reports directory.
+ try {
+ FileUtil.createFolder(new File(reportPathFormatString));
+ } catch (IOException ex) {
+ errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedMakeRptFolder"));
+ logger.log(Level.SEVERE, "Failed to make report folder, may be unable to generate reports.", ex); //NON-NLS
+ }
setupProgressPanel(generalReportModule);
ReportWorker worker = new ReportWorker(() -> {
- generalReportModule.generateReport(reportPath, progressPanel);
+ generalReportModule.generateReport(reportPathFormatString, progressPanel);
});
worker.execute();
displayProgressPanel();
@@ -164,9 +165,17 @@ class ReportGenerator {
*/
void generateTableReport(TableReportModule tableReport, Map artifactTypeSelections, Map tagNameSelections) {
if (tableReport != null && null != artifactTypeSelections) {
+ reportPathFormatString = String.format(reportPathFormatString, tableReport.getName());
+ // Create the root reports directory.
+ try {
+ FileUtil.createFolder(new File(reportPathFormatString));
+ } catch (IOException ex) {
+ errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedMakeRptFolder"));
+ logger.log(Level.SEVERE, "Failed to make report folder, may be unable to generate reports.", ex); //NON-NLS
+ }
setupProgressPanel(tableReport);
ReportWorker worker = new ReportWorker(() -> {
- tableReport.startReport(reportPath);
+ tableReport.startReport(reportPathFormatString);
TableReportGenerator generator = new TableReportGenerator(artifactTypeSelections, tagNameSelections, progressPanel, tableReport);
generator.execute();
tableReport.endReport();
@@ -187,6 +196,14 @@ class ReportGenerator {
*/
void generateFileListReport(FileReportModule fileReportModule, Map enabledInfo) {
if (fileReportModule != null && null != enabledInfo) {
+ reportPathFormatString = String.format(reportPathFormatString, fileReportModule.getName());
+ // Create the root reports directory.
+ try {
+ FileUtil.createFolder(new File(reportPathFormatString));
+ } catch (IOException ex) {
+ errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedMakeRptFolder"));
+ logger.log(Level.SEVERE, "Failed to make report folder, may be unable to generate reports.", ex); //NON-NLS
+ }
List enabled = new ArrayList<>();
for (Entry e : enabledInfo.entrySet()) {
if (e.getValue()) {
@@ -204,7 +221,7 @@ class ReportGenerator {
List files = getFiles();
int numFiles = files.size();
if (progressPanel.getStatus() != ReportStatus.CANCELED) {
- fileReportModule.startReport(reportPath);
+ fileReportModule.startReport(reportPathFormatString);
fileReportModule.startTable(enabled);
}
progressPanel.setIndeterminate(false);
@@ -262,7 +279,7 @@ class ReportGenerator {
private void setupProgressPanel(ReportModule module) {
String reportFilePath = module.getRelativeFilePath();
if (!reportFilePath.isEmpty()) {
- this.progressPanel = reportGenerationPanel.addReport(module.getName(), reportPath + reportFilePath);
+ this.progressPanel = reportGenerationPanel.addReport(module.getName(), String.format(reportPathFormatString, module.getName()) + reportFilePath);
} else {
this.progressPanel = reportGenerationPanel.addReport(module.getName(), null);
}
diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java
index 834fb69710..88d9fe44b1 100644
--- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java
+++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java
@@ -2,7 +2,7 @@
*
* Autopsy Forensic Browser
*
- * Copyright 2012-2014 Basis Technology Corp.
+ * Copyright 2012-2018 Basis Technology Corp.
*
* Copyright 2012 42six Solutions.
* Contact: aebadirad 42six com
@@ -72,6 +72,7 @@ class ReportHTML implements TableReportModule {
private static final String THUMBS_REL_PATH = "thumbs" + File.separator; //NON-NLS
private static ReportHTML instance;
private static final int MAX_THUMBS_PER_PAGE = 1000;
+ private static final String HTML_SUBDIR = "content";
private Case currentCase;
private SleuthkitCase skCase;
static Integer THUMBNAIL_COLUMNS = 5;
@@ -79,6 +80,7 @@ class ReportHTML implements TableReportModule {
private Map dataTypes;
private String path;
private String thumbsPath;
+ private String subPath;
private String currentDataType; // name of current data type
private Integer rowCount; // number of rows (aka artifacts or tags) for the current data type
private Writer out;
@@ -107,6 +109,7 @@ class ReportHTML implements TableReportModule {
path = "";
thumbsPath = "";
+ subPath = "";
currentDataType = "";
rowCount = 0;
@@ -157,7 +160,7 @@ class ReportHTML implements TableReportModule {
if (null != artifactType) {
// set the icon file name
iconFileName = dataTypeToFileName(artifactType.getDisplayName()) + ".png"; //NON-NLS
- iconFilePath = path + File.separator + iconFileName;
+ iconFilePath = subPath + File.separator + iconFileName;
// determine the source image to use
switch (artifactType) {
@@ -268,7 +271,7 @@ class ReportHTML implements TableReportModule {
logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = " + dataType); //NON-NLS
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS
iconFileName = "star.png"; //NON-NLS
- iconFilePath = path + File.separator + iconFileName;
+ iconFilePath = subPath + File.separator + iconFileName;
break;
}
} else if (dataType.startsWith(ARTIFACT_TYPE.TSK_ACCOUNT.getDisplayName())) {
@@ -281,11 +284,11 @@ class ReportHTML implements TableReportModule {
*/
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/accounts.png"); //NON-NLS
iconFileName = "accounts.png"; //NON-NLS
- iconFilePath = path + File.separator + iconFileName;
+ iconFilePath = subPath + File.separator + iconFileName;
} else { // no defined artifact found for this dataType
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS
iconFileName = "star.png"; //NON-NLS
- iconFilePath = path + File.separator + iconFileName;
+ iconFilePath = subPath + File.separator + iconFileName;
}
try {
@@ -325,10 +328,11 @@ class ReportHTML implements TableReportModule {
// Refresh the HTML report
refresh();
// Setup the path for the HTML report
- this.path = baseReportDir + "HTML Report" + File.separator; //NON-NLS
- this.thumbsPath = this.path + "thumbs" + File.separator; //NON-NLS
+ this.path = baseReportDir; //NON-NLS
+ this.subPath = this.path + HTML_SUBDIR + File.separator;
+ this.thumbsPath = this.subPath + THUMBS_REL_PATH; //NON-NLS
try {
- FileUtil.createFolder(new File(this.path));
+ FileUtil.createFolder(new File(this.subPath));
FileUtil.createFolder(new File(this.thumbsPath));
} catch (IOException ex) {
logger.log(Level.SEVERE, "Unable to make HTML report folder."); //NON-NLS
@@ -367,7 +371,7 @@ class ReportHTML implements TableReportModule {
public void startDataType(String name, String description) {
String title = dataTypeToFileName(name);
try {
- out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path + title + ".html"), "UTF-8")); //NON-NLS
+ out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(subPath + title + ".html"), "UTF-8")); //NON-NLS
} catch (FileNotFoundException ex) {
logger.log(Level.SEVERE, "File not found: {0}", ex); //NON-NLS
} catch (UnsupportedEncodingException ex) {
@@ -747,7 +751,7 @@ class ReportHTML implements TableReportModule {
// Make a folder for the local file with the same tagName as the tag.
StringBuilder localFilePath = new StringBuilder(); // full path
- localFilePath.append(path);
+ localFilePath.append(subPath);
localFilePath.append(dirName2);
File localFileFolder = new File(localFilePath.toString());
if (!localFileFolder.exists()) {
@@ -777,7 +781,7 @@ class ReportHTML implements TableReportModule {
}
// get the relative path
- return localFilePath.toString().substring(path.length());
+ return localFilePath.toString().substring(subPath.length());
}
/**
@@ -795,7 +799,7 @@ class ReportHTML implements TableReportModule {
@Override
public String getRelativeFilePath() {
- return "HTML Report" + File.separator + "index.html"; //NON-NLS
+ return "report.html"; //NON-NLS
}
@Override
@@ -814,7 +818,7 @@ class ReportHTML implements TableReportModule {
private void writeCss() {
Writer cssOut = null;
try {
- cssOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path + "index.css"), "UTF-8")); //NON-NLS NON-NLS
+ cssOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(subPath + "index.css"), "UTF-8")); //NON-NLS NON-NLS
String css = "body {margin: 0px; padding: 0px; background: #FFFFFF; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353;}\n"
+ //NON-NLS
"#content {padding: 30px;}\n"
@@ -875,7 +879,7 @@ class ReportHTML implements TableReportModule {
*/
private void writeIndex() {
Writer indexOut = null;
- String indexFilePath = path + "index.html"; //NON-NLS
+ String indexFilePath = path + "report.html"; //NON-NLS
try {
indexOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(indexFilePath), "UTF-8")); //NON-NLS
StringBuilder index = new StringBuilder();
@@ -883,7 +887,7 @@ class ReportHTML implements TableReportModule {
String iconPath = reportBranding.getAgencyLogoPath();
if (iconPath == null) {
// use default Autopsy icon if custom icon is not set
- iconPath = "favicon.ico";
+ iconPath = HTML_SUBDIR + "favicon.ico";
} else {
iconPath = Paths.get(reportBranding.getAgencyLogoPath()).getFileName().toString(); //ref to writeNav() for agency_logo
}
@@ -895,8 +899,8 @@ class ReportHTML implements TableReportModule {
index.append("\n"); //NON-NLS
index.append("\n"); //NON-NLS
index.append("