mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 07:56:16 +00:00
Interim check in of files for improved multiuser case dialog
This commit is contained in:
parent
eca1472d5b
commit
ac4637bf8c
@ -193,14 +193,10 @@ ReviewModeCasePanel.MetadataFileHeaderText=Metadata File
|
||||
OpenMultiUserCasePanel.jLabel1.text=Recent Cases
|
||||
OpenMultiUserCasePanel.openButton.text=Open
|
||||
OpenMultiUserCasePanel.cancelButton.text=Cancel
|
||||
MultiUserCasesPanel.bnOpen.text=&Open
|
||||
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=Select any case and start typing to search by case name
|
||||
MultiUserCasesPanel.cancelButton.text=Cancel
|
||||
ImageFilePanel.sectorSizeLabel.text=Sector size:
|
||||
LocalDiskPanel.sectorSizeLabel.text=Sector Size:
|
||||
LocalFilesPanel.displayNameLabel.text=Logical File Set Display Name: Default
|
||||
@ -241,3 +237,6 @@ ImageFilePanel.md5HashTextField.text=
|
||||
ImageFilePanel.errorLabel.text=Error Label
|
||||
ImageFilePanel.hashValuesNoteLabel.text=NOTE: These values will not be validated when the data source is added.
|
||||
ImageFilePanel.hashValuesLabel.text=Hash Values (optional):
|
||||
OpenMultiUserCasePanel.searchLabel.text=Select any case and start typing to search by case name
|
||||
|
||||
OpenMultiUserCasePanel.bnOpenSingleUserCase.text=Open Single-User Case...
|
||||
|
@ -32,6 +32,7 @@ import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
@ -57,6 +58,7 @@ import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
@ -80,6 +82,7 @@ import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.services.Services;
|
||||
import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchAction;
|
||||
import org.sleuthkit.autopsy.communications.OpenCommVisualizationToolAction;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
||||
@ -1956,9 +1959,9 @@ public class Case {
|
||||
}
|
||||
|
||||
if (isNewCase) {
|
||||
createCaseData(progressIndicator);
|
||||
createCaseData(progressIndicator); // RJCTODO: This name is vague
|
||||
} else {
|
||||
openCaseData(progressIndicator);
|
||||
openCaseData(progressIndicator); // RJCTODO: This name is vague
|
||||
}
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
@ -1970,6 +1973,7 @@ public class Case {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
|
||||
}
|
||||
|
||||
} catch (CaseActionException ex) {
|
||||
/*
|
||||
* Cancellation or failure. Clean up. The sleep is a little hack to
|
||||
@ -2003,6 +2007,19 @@ public class Case {
|
||||
"Case.exceptionMessage.couldNotCreateMetadataFile=Failed to create case metadata file."
|
||||
})
|
||||
private void createCaseData(ProgressIndicator progressIndicator) throws CaseActionException {
|
||||
// RJCTODO: progress
|
||||
try {
|
||||
CoordinationService coordinationService = CoordinationService.getInstance();
|
||||
CaseNodeData nodeData = new CaseNodeData(metadata);
|
||||
coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), nodeData.toArray());
|
||||
} catch (CoordinationServiceException | InterruptedException | ParseException ex) {
|
||||
// RJCTODO
|
||||
}
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the case directory, if it does not already exist.
|
||||
*
|
||||
@ -2014,6 +2031,10 @@ public class Case {
|
||||
Case.createCaseDirectory(metadata.getCaseDirectory(), metadata.getCaseType());
|
||||
}
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the case database.
|
||||
*/
|
||||
@ -2065,6 +2086,24 @@ public class Case {
|
||||
})
|
||||
private void openCaseData(ProgressIndicator progressIndicator) throws CaseActionException {
|
||||
try {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
|
||||
}
|
||||
|
||||
// RJCTODO: progress
|
||||
try {
|
||||
CoordinationService coordinationService = CoordinationService.getInstance();
|
||||
CaseNodeData nodeData = new CaseNodeData(coordinationService.getNodeData(CategoryNode.CASES, metadata.getCaseDirectory()));
|
||||
nodeData.setLastAccessDate(new Date());
|
||||
coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), nodeData.toArray());
|
||||
} catch (CoordinationServiceException | InterruptedException | CaseNodeData.InvalidDataException ex) {
|
||||
// RJCTODO
|
||||
}
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
|
||||
}
|
||||
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_openingCaseDatabase());
|
||||
String databaseName = metadata.getCaseDatabaseName();
|
||||
if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
|
||||
|
@ -1,265 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
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.io.IOException;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.util.NbBundle;
|
||||
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.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
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 LoadCaseListWorker 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_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 (user can unhide it)
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the .aut file for the selected case.
|
||||
*
|
||||
* @return the full path to the selected case's .aut file
|
||||
*/
|
||||
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 LoadCaseListWorker();
|
||||
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")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//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);
|
||||
}// </editor-fold>//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 List of cases in a background thread
|
||||
*/
|
||||
private class LoadCaseListWorker extends SwingWorker<Void, Void> {
|
||||
|
||||
private List<CaseMetadata> cases;
|
||||
|
||||
/**
|
||||
* Gets a list of the cases in the top level case folder
|
||||
*
|
||||
* @return List of cases.
|
||||
*
|
||||
* @throws CoordinationServiceException
|
||||
*/
|
||||
private List<CaseMetadata> getCases() throws CoordinationService.CoordinationServiceException {
|
||||
List<CaseMetadata> caseList = new ArrayList<>();
|
||||
List<String> nodeList = CoordinationService.getInstance().getNodeList(CoordinationService.CategoryNode.CASES);
|
||||
|
||||
for (String node : nodeList) {
|
||||
Path casePath;
|
||||
try {
|
||||
casePath = Paths.get(node).toRealPath(LinkOption.NOFOLLOW_LINKS);
|
||||
|
||||
File caseFolder = casePath.toFile();
|
||||
if (caseFolder.exists()) {
|
||||
/*
|
||||
* Search for '*.aut' files.
|
||||
*/
|
||||
File[] fileArray = caseFolder.listFiles();
|
||||
if (fileArray == null) {
|
||||
continue;
|
||||
}
|
||||
String autFilePath = null;
|
||||
for (File file : fileArray) {
|
||||
String name = file.getName().toLowerCase();
|
||||
if (autFilePath == null && name.endsWith(".aut")) {
|
||||
try {
|
||||
caseList.add(new CaseMetadata(Paths.get(file.getAbsolutePath())));
|
||||
} catch (CaseMetadata.CaseMetadataException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Error reading case metadata file '%s'.", autFilePath), ex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ignore) {
|
||||
//if a path could not be resolved to a real path do add it to the caseList
|
||||
}
|
||||
}
|
||||
return caseList;
|
||||
}
|
||||
|
||||
@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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -52,7 +52,8 @@ import org.xml.sax.SAXException;
|
||||
public final class CaseMetadata {
|
||||
|
||||
private static final String FILE_EXTENSION = ".aut";
|
||||
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss (z)");
|
||||
private static final String DATE_FORMAT_STRING = "yyyy/MM/dd HH:mm:ss (z)";
|
||||
private static final DateFormat DATE_FORMAT = new SimpleDateFormat(DATE_FORMAT_STRING);
|
||||
|
||||
/*
|
||||
* Fields from schema version 1
|
||||
@ -92,6 +93,7 @@ public final class CaseMetadata {
|
||||
private final static String EXAMINER_ELEMENT_PHONE = "ExaminerPhone"; //NON-NLS
|
||||
private final static String EXAMINER_ELEMENT_EMAIL = "ExaminerEmail"; //NON-NLS
|
||||
private final static String CASE_ELEMENT_NOTES = "CaseNotes"; //NON-NLS
|
||||
|
||||
/*
|
||||
* Unread fields, regenerated on save.
|
||||
*/
|
||||
@ -119,6 +121,15 @@ public final class CaseMetadata {
|
||||
return FILE_EXTENSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date format used for dates in case metadata.
|
||||
*
|
||||
* @return The date format.
|
||||
*/
|
||||
public static DateFormat getDateFormat() {
|
||||
return new SimpleDateFormat(DATE_FORMAT_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a CaseMetadata object for a new case. The metadata is not
|
||||
* persisted to the case metadata file until writeFile or a setX method is
|
||||
@ -295,7 +306,7 @@ public final class CaseMetadata {
|
||||
*
|
||||
* @return The date this case was created, as a string.
|
||||
*/
|
||||
String getCreatedDate() {
|
||||
public String getCreatedDate() {
|
||||
return createdDate;
|
||||
}
|
||||
|
||||
|
@ -161,7 +161,7 @@ public final class CaseOpenAction extends CallableSystemAction implements Action
|
||||
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
|
||||
if (multiUserCaseWindow == null) {
|
||||
multiUserCaseWindow = MultiUserCasesDialog.getInstance();
|
||||
multiUserCaseWindow = OpenMultiUserCaseDialog.getInstance();
|
||||
}
|
||||
multiUserCaseWindow.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
|
||||
multiUserCaseWindow.setVisible(true);
|
||||
|
198
Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseNode.java
Executable file
198
Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseNode.java
Executable file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
import java.awt.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.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
|
||||
/**
|
||||
* A NetBeans Explorer View node that represents a multi-user case.
|
||||
*/
|
||||
final class MultiUserCaseNode extends AbstractNode {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(MultiUserCaseNode.class.getName());
|
||||
private static final String CASE_AUTO_INGEST_LOG_FILE_NAME = "auto_ingest_log.txt";
|
||||
private final CaseNodeData caseNodeData;
|
||||
private final Path caseMetadataFilePath;
|
||||
private final Path caseAutoIngestLogFilePath;
|
||||
|
||||
/**
|
||||
* Constructs a NetBeans Explorer View node that represents a multi-user
|
||||
* case.
|
||||
*
|
||||
* @param caseNodeData The coordination service node data for the case.
|
||||
*/
|
||||
MultiUserCaseNode(CaseNodeData caseNodeData) {
|
||||
super(Children.LEAF);
|
||||
super.setName(caseNodeData.getDisplayName());
|
||||
setName(caseNodeData.getDisplayName());
|
||||
setDisplayName(caseNodeData.getDisplayName());
|
||||
this.caseNodeData = caseNodeData;
|
||||
this.caseMetadataFilePath = Paths.get(caseNodeData.getDirectory().toString(), caseNodeData.getName() + CaseMetadata.getFileExtension());
|
||||
this.caseAutoIngestLogFilePath = Paths.get(caseNodeData.getDirectory().toString(), CASE_AUTO_INGEST_LOG_FILE_NAME);
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"CaseNode.column.name=Name",
|
||||
"CaseNode.column.createTime=Create Time",
|
||||
"CaseNode.column.path=Path"
|
||||
})
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
if (sheetSet == null) {
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_name(),
|
||||
Bundle.CaseNode_column_name(),
|
||||
Bundle.CaseNode_column_name(),
|
||||
caseNodeData.getDisplayName()));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_createTime(),
|
||||
Bundle.CaseNode_column_createTime(),
|
||||
Bundle.CaseNode_column_createTime(),
|
||||
caseNodeData.getCreateDate()));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_path(),
|
||||
Bundle.CaseNode_column_path(),
|
||||
Bundle.CaseNode_column_path(),
|
||||
caseNodeData.getDirectory().toString()));
|
||||
return sheet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action[] getActions(boolean context) {
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new OpenMultiUserCaseAction(this.caseMetadataFilePath));
|
||||
actions.add(new OpenCaseAutoIngestLogAction(this.caseAutoIngestLogFilePath));
|
||||
return actions.toArray(new Action[actions.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action getPreferredAction() {
|
||||
return new OpenMultiUserCaseAction(this.caseMetadataFilePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* An action that opens the specified case and hides the multi-user case
|
||||
* panel.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"MultiUserNode.OpenMultiUserCaseAction.menuItemText=Open Case",
|
||||
"# {0} - caseErrorMessage", "MultiUserNode.OpenMultiUserCaseAction.caseOpeningErrorErrorMsg=Failed to open case: {0}"
|
||||
})
|
||||
private static final class OpenMultiUserCaseAction extends AbstractAction {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Path caseMetadataFilePath;
|
||||
|
||||
OpenMultiUserCaseAction(Path caseMetadataFilePath) {
|
||||
super(Bundle.MultiUserNode_OpenMultiUserCaseAction_menuItemText());
|
||||
this.caseMetadataFilePath = caseMetadataFilePath;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
StartupWindowProvider.getInstance().close();
|
||||
OpenMultiUserCaseDialog.getInstance().setVisible(false);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Case.openAsCurrentCase(caseMetadataFilePath.toString());
|
||||
} 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
|
||||
/*
|
||||
* CaseActionException error messages are user-friendly.
|
||||
*/
|
||||
MessageNotifyUtil.Message.error(Bundle.MultiUserNode_OpenMultiUserCaseAction_caseOpeningErrorErrorMsg(ex.getLocalizedMessage()));
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
StartupWindowProvider.getInstance().open();
|
||||
OpenMultiUserCaseDialog.getInstance().setVisible(true);
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
super.clone();
|
||||
throw new CloneNotSupportedException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* An action that opens the case auto ingest log for the specified case.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"MultiUserNode.OpenCaseAutoIngestLogAction.menuItemText=Open Auto Ingest Log File",
|
||||
"MultiUserNode.OpenCaseAutoIngestLogAction.deletedLogErrorMsg=The case auto ingest log has been deleted.",
|
||||
"MultiUserNode.OpenCaseAutoIngestLogAction.logOpenFailedErrorMsg=Failed to open case auto ingest log. See application log for details.",
|
||||
})
|
||||
private static final class OpenCaseAutoIngestLogAction extends AbstractAction {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Path caseAutoIngestLogFilePath;
|
||||
|
||||
OpenCaseAutoIngestLogAction(Path caseAutoIngestLogFilePath) {
|
||||
super(Bundle.MultiUserNode_OpenCaseAutoIngestLogAction_menuItemText());
|
||||
this.caseAutoIngestLogFilePath = caseAutoIngestLogFilePath;
|
||||
this.setEnabled(caseAutoIngestLogFilePath.toFile().exists());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
try {
|
||||
if (caseAutoIngestLogFilePath.toFile().exists()) {
|
||||
Desktop.getDesktop().edit(caseAutoIngestLogFilePath.toFile());
|
||||
} else {
|
||||
MessageNotifyUtil.Message.error(Bundle.MultiUserNode_OpenCaseAutoIngestLogAction_deletedLogErrorMsg());
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error opening case auto ingest log file at %s", caseAutoIngestLogFilePath), ex); //NON-NLS
|
||||
MessageNotifyUtil.Message.error(Bundle.MultiUserNode_OpenCaseAutoIngestLogAction_logOpenFailedErrorMsg());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
super.clone();
|
||||
throw new CloneNotSupportedException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
import javax.swing.ListSelectionModel;
|
||||
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 java.awt.EventQueue;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import org.openide.explorer.view.OutlineView;
|
||||
|
||||
/**
|
||||
* A JPanel with a scroll pane child component that contains a NetBeans
|
||||
* OutlineView that can be used to display a list of the multi-user cases known
|
||||
* to the coordination service.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // Matisse-generated UI widgets cause lots of false positives
|
||||
class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerManager.Provider {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger logger = Logger.getLogger(MultiUserCasesBrowserPanel.class.getName());
|
||||
private ExplorerManager explorerManager; // RJCTODO: COnsider making this final
|
||||
private final Outline outline;
|
||||
private final OutlineView outlineView;
|
||||
private LoadCaseListWorker loadCaseListWorker;
|
||||
|
||||
/**
|
||||
* Constructs a JPanel with a scroll pane child component that contains a
|
||||
* NetBeans OutlineView that can be used to display a list of the multi-user
|
||||
* cases known to the coordination service.
|
||||
*/
|
||||
MultiUserCasesBrowserPanel() {
|
||||
outlineView = new org.openide.explorer.view.OutlineView();
|
||||
initComponents();
|
||||
outline = outlineView.getOutline();
|
||||
customizeOutlineView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the the table of cases and its columns.
|
||||
*/
|
||||
private void customizeOutlineView() {
|
||||
outlineView.setPropertyColumns(
|
||||
Bundle.CaseNode_column_createTime(), Bundle.CaseNode_column_createTime(), // RJCTODO: Move these into this file?
|
||||
Bundle.CaseNode_column_path(), Bundle.CaseNode_column_path());
|
||||
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.CaseNode_column_name());
|
||||
outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
TableColumnModel columnModel = outline.getColumnModel();
|
||||
int pathColumnIndex = 0;
|
||||
int dateColumnIndex = 0;
|
||||
for (int index = 0; index < columnModel.getColumnCount(); index++) {
|
||||
if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.CaseNode_column_path())) {
|
||||
pathColumnIndex = index;
|
||||
} else if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.CaseNode_column_createTime())) {
|
||||
dateColumnIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Hide path column by default (user can unhide it)
|
||||
*/
|
||||
ETableColumn column = (ETableColumn) columnModel.getColumn(pathColumnIndex);
|
||||
((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 == explorerManager) {
|
||||
explorerManager = new ExplorerManager();
|
||||
}
|
||||
|
||||
caseTableScrollPane.setViewportView(outlineView);
|
||||
this.setVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplorerManager getExplorerManager() {
|
||||
return explorerManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of cases known to the review mode cases manager and
|
||||
* refreshes the cases table.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"MultiUserCasesBrowserPanel.waitNode.message=Please Wait..."
|
||||
})
|
||||
void refreshCases() {
|
||||
if (loadCaseListWorker == null || loadCaseListWorker.isDone()) {
|
||||
/*
|
||||
* RJCTODO: Explain this or move the data fetching into the create
|
||||
* keys method of the nodes...
|
||||
*/
|
||||
EmptyNode emptyNode = new EmptyNode(Bundle.MultiUserCasesBrowserPanel_waitNode_message());
|
||||
explorerManager.setRootContext(emptyNode);
|
||||
|
||||
loadCaseListWorker = new LoadCaseListWorker();
|
||||
loadCaseListWorker.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")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//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);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JScrollPane caseTableScrollPane;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
/**
|
||||
* A background task that gets the multi-user case data from the case
|
||||
* directory lock coordination service nodes.
|
||||
*/
|
||||
private class LoadCaseListWorker extends SwingWorker<List<CaseNodeData>, Void> {
|
||||
|
||||
private static final String CASE_AUTO_INGEST_LOG_NAME = "AUTO_INGEST_LOG.TXT"; //NON-NLS
|
||||
private static final String RESOURCES_LOCK_SUFFIX = "_RESOURCES"; //NON-NLS
|
||||
|
||||
@Override
|
||||
protected List<CaseNodeData> doInBackground() throws Exception {
|
||||
final List<CaseNodeData> cases = new ArrayList<>();
|
||||
final CoordinationService coordinationService = CoordinationService.getInstance();
|
||||
final List<String> nodeList = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES);
|
||||
for (String nodeName : nodeList) {
|
||||
/*
|
||||
* Ignore case name lock nodes.
|
||||
*/
|
||||
final Path nodeNameAsPath = Paths.get(nodeName);
|
||||
if (!(nodeNameAsPath.toString().contains("\\") || nodeNameAsPath.toString().contains("//"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore case auto ingest log lock nodes and resource lock
|
||||
* nodes.
|
||||
*/
|
||||
final String lastNodeNameComponent = nodeNameAsPath.getFileName().toString();
|
||||
if (lastNodeNameComponent.equals(CASE_AUTO_INGEST_LOG_NAME) || lastNodeNameComponent.endsWith(RESOURCES_LOCK_SUFFIX)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the data from the case directory lock node. This data may not exist
|
||||
* for "legacy" nodes. If it is missing, create it.
|
||||
*/
|
||||
try {
|
||||
CaseNodeData nodeData;
|
||||
byte[] nodeBytes = CoordinationService.getInstance().getNodeData(CoordinationService.CategoryNode.CASES, nodeName);
|
||||
if (nodeBytes != null && nodeBytes.length > 0) {
|
||||
nodeData = new CaseNodeData(nodeBytes);
|
||||
if (nodeData.getVersion() > 0) {
|
||||
cases.add(nodeData);
|
||||
} else {
|
||||
nodeData = createNodeDataFromCaseMetadata(nodeName);
|
||||
}
|
||||
} else {
|
||||
nodeData = createNodeDataFromCaseMetadata(nodeName);
|
||||
}
|
||||
cases.add(nodeData);
|
||||
|
||||
} catch (CoordinationService.CoordinationServiceException | CaseNodeData.InvalidDataException | IOException | CaseMetadata.CaseMetadataException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error getting coordination service node data for %s", nodeName), ex);
|
||||
}
|
||||
|
||||
}
|
||||
return cases;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
try {
|
||||
final List<CaseNodeData> cases = get();
|
||||
EventQueue.invokeLater(() -> {
|
||||
MultiUserCasesRootNode caseListNode = new MultiUserCasesRootNode(cases);
|
||||
explorerManager.setRootContext(caseListNode);
|
||||
});
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.SEVERE, "Unexpected interrupt during background processing", ex);
|
||||
} catch (ExecutionException ex) {
|
||||
logger.log(Level.SEVERE, "Error during background processing", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private CaseNodeData createNodeDataFromCaseMetadata(String nodeName) throws IOException, CaseMetadata.CaseMetadataException, ParseException, CoordinationService.CoordinationServiceException, InterruptedException {
|
||||
Path caseDirectoryPath = Paths.get(nodeName).toRealPath(LinkOption.NOFOLLOW_LINKS);
|
||||
File caseDirectory = caseDirectoryPath.toFile();
|
||||
if (caseDirectory.exists()) {
|
||||
File[] files = caseDirectory.listFiles();
|
||||
for (File file : files) {
|
||||
String name = file.getName().toLowerCase();
|
||||
if (name.endsWith(CaseMetadata.getFileExtension())) {
|
||||
CaseMetadata metadata = new CaseMetadata(Paths.get(file.getAbsolutePath()));
|
||||
CaseNodeData nodeData = new CaseNodeData(metadata);
|
||||
CoordinationService coordinationService = CoordinationService.getInstance();
|
||||
coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodeName, nodeData.toArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IOException(String.format("Could not find case metadata file for %s", nodeName));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
import java.awt.Dialog;
|
||||
import java.awt.event.KeyEvent;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.KeyStroke;
|
||||
import org.openide.windows.WindowManager;
|
||||
|
||||
/**
|
||||
* This class extends a JDialog and maintains the MultiUserCasesPanel.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
final class MultiUserCasesDialog extends JDialog {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String REVIEW_MODE_TITLE = "Open Multi-User Case";
|
||||
private static MultiUserCasesPanel multiUserCasesPanel;
|
||||
private static MultiUserCasesDialog instance;
|
||||
|
||||
/**
|
||||
* Gets the instance of the MultiuserCasesDialog.
|
||||
*
|
||||
* @return The instance.
|
||||
*/
|
||||
static public MultiUserCasesDialog getInstance() {
|
||||
if(instance == null) {
|
||||
instance = new MultiUserCasesDialog();
|
||||
instance.init();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a MultiUserCasesDialog object.
|
||||
*/
|
||||
private MultiUserCasesDialog() {
|
||||
super(WindowManager.getDefault().getMainWindow(),
|
||||
REVIEW_MODE_TITLE,
|
||||
Dialog.ModalityType.APPLICATION_MODAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the multi-user cases panel.
|
||||
*/
|
||||
private void init() {
|
||||
getRootPane().registerKeyboardAction(
|
||||
e -> {
|
||||
setVisible(false);
|
||||
},
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
|
||||
multiUserCasesPanel = new MultiUserCasesPanel(this);
|
||||
add(multiUserCasesPanel);
|
||||
pack();
|
||||
setResizable(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dialog visibility. When setting it to visible, the contents will
|
||||
* refresh.
|
||||
*
|
||||
* @param value True or false.
|
||||
*/
|
||||
@Override
|
||||
public void setVisible(boolean value) {
|
||||
if(value) {
|
||||
multiUserCasesPanel.refresh();
|
||||
}
|
||||
super.setVisible(value);
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
import java.util.List;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* A root node for displaying MultiUserCaseNodes in a NetBeans Explorer View.
|
||||
*/
|
||||
final class MultiUserCasesRootNode extends AbstractNode {
|
||||
|
||||
/**
|
||||
* Constructs a root node for displaying MultiUserCaseNodes in a NetBeans
|
||||
* Explorer View.
|
||||
*
|
||||
* @param case A list of coordination service node data objects representing
|
||||
* multi-user cases.
|
||||
*/
|
||||
MultiUserCasesRootNode(List<CaseNodeData> cases) {
|
||||
super(Children.create(new MultiUserCasesRootNodeChildren(cases), true));
|
||||
}
|
||||
|
||||
private static class MultiUserCasesRootNodeChildren extends ChildFactory<CaseNodeData> {
|
||||
|
||||
private final List<CaseNodeData> cases;
|
||||
|
||||
MultiUserCasesRootNodeChildren(List<CaseNodeData> cases) {
|
||||
this.cases = cases;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<CaseNodeData> keys) {
|
||||
if (cases != null && cases.size() > 0) {
|
||||
keys.addAll(cases);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(CaseNodeData key) {
|
||||
return new MultiUserCaseNode(key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,232 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
import java.awt.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.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.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 caseList the list of CaseMetadata objects representing the cases
|
||||
*/
|
||||
MultiUserNode(List<CaseMetadata> caseList) {
|
||||
super(Children.create(new MultiUserNodeChildren(caseList), true));
|
||||
}
|
||||
|
||||
static class MultiUserNodeChildren extends ChildFactory<CaseMetadata> {
|
||||
|
||||
private final List<CaseMetadata> caseList;
|
||||
|
||||
MultiUserNodeChildren(List<CaseMetadata> caseList) {
|
||||
this.caseList = caseList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<CaseMetadata> list) {
|
||||
if (caseList != null && caseList.size() > 0) {
|
||||
list.addAll(caseList);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(CaseMetadata 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 Path caseLogFilePath;
|
||||
|
||||
MultiUserCaseNode(CaseMetadata multiUserCase) {
|
||||
super(Children.LEAF);
|
||||
caseName = multiUserCase.getCaseDisplayName();
|
||||
caseCreatedDate = multiUserCase.getCreatedDate();
|
||||
super.setName(caseName);
|
||||
setName(caseName);
|
||||
setDisplayName(caseName);
|
||||
caseMetadataFilePath = multiUserCase.getFilePath().toString();
|
||||
caseLogFilePath = Paths.get(multiUserCase.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);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
if (sheetSet == null) {
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_name(), Bundle.CaseNode_column_name(), Bundle.CaseNode_column_name(),
|
||||
caseName));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_createdTime(), Bundle.CaseNode_column_createdTime(), Bundle.CaseNode_column_createdTime(),
|
||||
caseCreatedDate));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_metadataFilePath(), Bundle.CaseNode_column_metadataFilePath(), Bundle.CaseNode_column_metadataFilePath(),
|
||||
caseMetadataFilePath));
|
||||
return sheet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action[] getActions(boolean context) {
|
||||
List<Action> 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.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
import java.awt.Dialog;
|
||||
import java.awt.event.KeyEvent;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.KeyStroke;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.windows.WindowManager;
|
||||
|
||||
/**
|
||||
* A singleton JDialog that allows a user to open a multi-user case.
|
||||
*/
|
||||
final class OpenMultiUserCaseDialog extends JDialog {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static OpenMultiUserCaseDialog instance;
|
||||
private static OpenMultiUserCasePanel multiUserCasesPanel;
|
||||
|
||||
/**
|
||||
* Gets the singleton JDialog singleton JDialog that allows a user to open a
|
||||
* multi-user case.
|
||||
*
|
||||
* @return The singleton JDialog instance.
|
||||
*/
|
||||
public synchronized static OpenMultiUserCaseDialog getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new OpenMultiUserCaseDialog();
|
||||
instance.init();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a singleton JDialog singleton JDialog that allows a user to
|
||||
* open a multi-user case.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"OpenMultiUserCaseDialog.title=Open Multi-User Case"
|
||||
})
|
||||
private OpenMultiUserCaseDialog() {
|
||||
super(WindowManager.getDefault().getMainWindow(),
|
||||
Bundle.OpenMultiUserCaseDialog_title(),
|
||||
Dialog.ModalityType.APPLICATION_MODAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a keyboard action to hide the dialog when the escape key is
|
||||
* pressed and adds a OpenMultiUserCasePanel child component.
|
||||
*/
|
||||
private void init() {
|
||||
getRootPane().registerKeyboardAction(
|
||||
action -> {
|
||||
setVisible(false);
|
||||
},
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
|
||||
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
multiUserCasesPanel = new OpenMultiUserCasePanel(this);
|
||||
add(multiUserCasesPanel);
|
||||
pack();
|
||||
setResizable(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the dialog visibility. When made visible, the dialog refreshes the
|
||||
* display of its OpenMultiUserCasePanel child component.
|
||||
*
|
||||
* @param value True or false.
|
||||
*/
|
||||
@Override
|
||||
public void setVisible(boolean value) {
|
||||
if (value) {
|
||||
multiUserCasesPanel.refreshDisplay();
|
||||
}
|
||||
super.setVisible(value);
|
||||
}
|
||||
|
||||
}
|
@ -28,12 +28,10 @@
|
||||
<Component id="caseExplorerScrollPane" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="searchLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<EmptySpace pref="350" max="32767" attributes="0"/>
|
||||
<Component id="bnOpenSingleUserCase" min="-2" pref="192" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="190" max="-2" attributes="0"/>
|
||||
<Component id="bnOpen" linkSize="1" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="cancelButton" linkSize="1" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="cancelButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
@ -48,7 +46,6 @@
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="bnOpen" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="bnOpenSingleUserCase" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="searchLabel" alignment="3" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
@ -58,30 +55,10 @@
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="bnOpen">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.bnOpen.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[80, 23]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[80, 23]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[80, 23]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnOpenActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="bnOpenSingleUserCase">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.bnOpenSingleUserCase.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="OpenMultiUserCasePanel.bnOpenSingleUserCase.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[156, 23]"/>
|
||||
@ -97,7 +74,7 @@
|
||||
<Component class="javax.swing.JButton" name="cancelButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.cancelButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="OpenMultiUserCasePanel.cancelButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[80, 23]"/>
|
||||
@ -116,7 +93,7 @@
|
||||
<Component class="javax.swing.JLabel" name="searchLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.searchLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="OpenMultiUserCasePanel.searchLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2017-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,107 +18,52 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
import java.awt.Cursor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SortOrder;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import javax.swing.table.TableRowSorter;
|
||||
import org.openide.util.Lookup;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
|
||||
/**
|
||||
* A panel that allows a user to open cases created by auto ingest.
|
||||
* A JPanel that allows a user to open a multi-user case.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
final class MultiUserCasesPanel extends JPanel{
|
||||
@SuppressWarnings("PMD.SingularField") // Matisse-generated UI widgets cause lots of false positives
|
||||
final class OpenMultiUserCasePanel extends JPanel {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(MultiUserCasesPanel.class.getName());
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final JDialog parentDialog;
|
||||
private final CaseBrowser caseBrowserPanel;
|
||||
private final MultiUserCasesBrowserPanel caseBrowserPanel;
|
||||
|
||||
/**
|
||||
* Constructs a panel that allows a user to open cases created by automated
|
||||
* ingest.
|
||||
* Constructs a JPanel that allows a user to open a multi-user case.
|
||||
*
|
||||
* @param parentDialog The parent dialog of the panel, may be null. If
|
||||
* provided, the dialog is hidden when this poanel's
|
||||
* cancel button is pressed.
|
||||
*/
|
||||
MultiUserCasesPanel(JDialog parentDialog) {
|
||||
OpenMultiUserCasePanel(JDialog parentDialog) {
|
||||
this.parentDialog = parentDialog;
|
||||
initComponents();
|
||||
|
||||
caseBrowserPanel = new CaseBrowser();
|
||||
caseBrowserPanel = new MultiUserCasesBrowserPanel();
|
||||
caseExplorerScrollPane.add(caseBrowserPanel);
|
||||
caseExplorerScrollPane.setViewportView(caseBrowserPanel);
|
||||
/*
|
||||
* Listen for row selection changes and set button state for the current
|
||||
* selection.
|
||||
*/
|
||||
caseBrowserPanel.addListSelectionListener((ListSelectionEvent e) -> {
|
||||
setButtons();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of cases known to the review mode cases manager and
|
||||
* refreshes the cases table.
|
||||
* Refreshes the child component that displays the multi-user cases known to
|
||||
* the coordination service..
|
||||
*/
|
||||
void refresh() {
|
||||
caseBrowserPanel.refresh();
|
||||
void refreshDisplay() {
|
||||
caseBrowserPanel.refreshCases();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/disables the Open and Show Log buttons based on the case selected
|
||||
* in the cases table.
|
||||
*/
|
||||
void setButtons() {
|
||||
bnOpen.setEnabled(caseBrowserPanel.isRowSelected());
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a case.
|
||||
*
|
||||
* @param caseMetadataFilePath The path to the case metadata file.
|
||||
*/
|
||||
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 {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RowSorter which makes columns whose type is Date to be sorted first in
|
||||
* Descending order then in Ascending order
|
||||
* A RowSorter which causes columns whose type is Date to be sorted in
|
||||
* either descending order or ascending order with descending order as the
|
||||
* default.
|
||||
*/
|
||||
private static class RowSorter<M extends DefaultTableModel> extends TableRowSorter<M> {
|
||||
|
||||
@ -155,7 +100,6 @@ final class MultiUserCasesPanel extends JPanel{
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
bnOpen = new javax.swing.JButton();
|
||||
bnOpenSingleUserCase = new javax.swing.JButton();
|
||||
cancelButton = new javax.swing.JButton();
|
||||
searchLabel = new javax.swing.JLabel();
|
||||
@ -164,18 +108,7 @@ final class MultiUserCasesPanel extends JPanel{
|
||||
setName("Completed Cases"); // NOI18N
|
||||
setPreferredSize(new java.awt.Dimension(960, 485));
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(bnOpen, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnOpen.text")); // NOI18N
|
||||
bnOpen.setEnabled(false);
|
||||
bnOpen.setMaximumSize(new java.awt.Dimension(80, 23));
|
||||
bnOpen.setMinimumSize(new java.awt.Dimension(80, 23));
|
||||
bnOpen.setPreferredSize(new java.awt.Dimension(80, 23));
|
||||
bnOpen.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
bnOpenActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(bnOpenSingleUserCase, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnOpenSingleUserCase.text")); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(bnOpenSingleUserCase, org.openide.util.NbBundle.getMessage(OpenMultiUserCasePanel.class, "OpenMultiUserCasePanel.bnOpenSingleUserCase.text")); // NOI18N
|
||||
bnOpenSingleUserCase.setMinimumSize(new java.awt.Dimension(156, 23));
|
||||
bnOpenSingleUserCase.setPreferredSize(new java.awt.Dimension(156, 23));
|
||||
bnOpenSingleUserCase.addActionListener(new java.awt.event.ActionListener() {
|
||||
@ -184,7 +117,7 @@ final class MultiUserCasesPanel extends JPanel{
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.cancelButton.text")); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(OpenMultiUserCasePanel.class, "OpenMultiUserCasePanel.cancelButton.text")); // NOI18N
|
||||
cancelButton.setMaximumSize(new java.awt.Dimension(80, 23));
|
||||
cancelButton.setMinimumSize(new java.awt.Dimension(80, 23));
|
||||
cancelButton.setPreferredSize(new java.awt.Dimension(80, 23));
|
||||
@ -194,7 +127,7 @@ final class MultiUserCasesPanel extends JPanel{
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(searchLabel, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.searchLabel.text")); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(searchLabel, org.openide.util.NbBundle.getMessage(OpenMultiUserCasePanel.class, "OpenMultiUserCasePanel.searchLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
@ -206,17 +139,12 @@ final class MultiUserCasesPanel extends JPanel{
|
||||
.addComponent(caseExplorerScrollPane)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(searchLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 350, Short.MAX_VALUE)
|
||||
.addComponent(bnOpenSingleUserCase, javax.swing.GroupLayout.PREFERRED_SIZE, 192, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(190, 190, 190)
|
||||
.addComponent(bnOpen, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(cancelButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {bnOpen, cancelButton});
|
||||
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
@ -225,7 +153,6 @@ final class MultiUserCasesPanel extends JPanel{
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(cancelButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(bnOpen, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(bnOpenSingleUserCase, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(searchLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
@ -233,14 +160,10 @@ final class MultiUserCasesPanel extends JPanel{
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
/**
|
||||
* Open button action
|
||||
* Opens the standard open single user case window.
|
||||
*
|
||||
* @param evt -- The event that caused this to be called
|
||||
* @param evt An ActionEvent, unused.
|
||||
*/
|
||||
private void bnOpenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOpenActionPerformed
|
||||
openCase(caseBrowserPanel.getCasePath());
|
||||
}//GEN-LAST:event_bnOpenActionPerformed
|
||||
|
||||
private void bnOpenSingleUserCaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOpenSingleUserCaseActionPerformed
|
||||
Lookup.getDefault().lookup(CaseOpenAction.class).openCaseSelectionWindow();
|
||||
}//GEN-LAST:event_bnOpenSingleUserCaseActionPerformed
|
||||
@ -252,7 +175,6 @@ final class MultiUserCasesPanel extends JPanel{
|
||||
}//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 cancelButton;
|
||||
private javax.swing.JScrollPane caseExplorerScrollPane;
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2017 Basis Technology Corp.
|
||||
* Copyright 2017-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -20,21 +20,40 @@ package org.sleuthkit.autopsy.coordinationservice;
|
||||
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
|
||||
|
||||
/**
|
||||
* An object that converts case data for a case directory coordination service
|
||||
* An object that converts data for a case directory lock coordination service
|
||||
* node to and from byte arrays.
|
||||
*/
|
||||
public final class CaseNodeData {
|
||||
|
||||
private static final int CURRENT_VERSION = 0;
|
||||
private static final int CURRENT_VERSION = 1;
|
||||
|
||||
/*
|
||||
* Version 0 fields.
|
||||
*/
|
||||
private int version;
|
||||
private boolean errorsOccurred;
|
||||
|
||||
/*
|
||||
* Version 1 fields.
|
||||
*/
|
||||
private Path directory;
|
||||
private long createDate;
|
||||
private long lastAccessDate;
|
||||
private String name;
|
||||
private String displayName;
|
||||
private short deletedItemFlags;
|
||||
|
||||
/**
|
||||
* Gets the current version of the case directory coordination service node
|
||||
* data.
|
||||
* Gets the current version of the case directory lock coordination service
|
||||
* node data.
|
||||
*
|
||||
* @return The version number.
|
||||
*/
|
||||
@ -42,9 +61,30 @@ public final class CaseNodeData {
|
||||
return CaseNodeData.CURRENT_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses a CaseMetadata object to construct an object that converts data for
|
||||
* a case directory lock coordination service node to and from byte arrays.
|
||||
*
|
||||
* @param metadata The case meta data.
|
||||
*
|
||||
* @throws java.text.ParseException If there is an error parsing dates from
|
||||
* string representations of dates in the
|
||||
* meta data.
|
||||
*/
|
||||
public CaseNodeData(CaseMetadata metadata) throws ParseException {
|
||||
this.version = CURRENT_VERSION;
|
||||
this.errorsOccurred = false;
|
||||
this.directory = Paths.get(metadata.getCaseDirectory());
|
||||
this.createDate = CaseMetadata.getDateFormat().parse(metadata.getCreatedDate()).getTime();
|
||||
this.lastAccessDate = new Date().getTime(); // Don't really know.
|
||||
this.name = metadata.getCaseName();
|
||||
this.displayName = metadata.getCaseDisplayName();
|
||||
this.deletedItemFlags = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses coordination service node data to construct an object that converts
|
||||
* case data for a case directory coordination service node to and from byte
|
||||
* data for a case directory lock coordination service node to and from byte
|
||||
* arrays.
|
||||
*
|
||||
* @param nodeData The raw bytes received from the coordination service.
|
||||
@ -53,34 +93,56 @@ public final class CaseNodeData {
|
||||
* expected.
|
||||
*/
|
||||
public CaseNodeData(byte[] nodeData) throws InvalidDataException {
|
||||
if(nodeData == null || nodeData.length == 0) {
|
||||
this.version = CURRENT_VERSION;
|
||||
this.errorsOccurred = false;
|
||||
} else {
|
||||
if (nodeData == null || nodeData.length == 0) {
|
||||
throw new InvalidDataException(null == nodeData ? "Null node data byte array" : "Zero-length node data byte array");
|
||||
}
|
||||
|
||||
/*
|
||||
* Get fields from node data.
|
||||
* Get the fields from the node data.
|
||||
*/
|
||||
ByteBuffer buffer = ByteBuffer.wrap(nodeData);
|
||||
try {
|
||||
if (buffer.hasRemaining()) {
|
||||
/*
|
||||
* Get version 0 fields.
|
||||
*/
|
||||
this.version = buffer.getInt();
|
||||
|
||||
/*
|
||||
* Flags bit format: 76543210
|
||||
* 0-6 --> reserved for future use
|
||||
* 7 --> errorsOccurred
|
||||
* Flags bit format: 76543210 0-6 --> reserved for future use 7 -->
|
||||
* errorsOccurred
|
||||
*/
|
||||
byte flags = buffer.get();
|
||||
this.errorsOccurred = (flags < 0);
|
||||
|
||||
if (buffer.hasRemaining()) {
|
||||
/*
|
||||
* Get version 1 fields.
|
||||
*/
|
||||
this.directory = Paths.get(NodeDataUtils.getStringFromBuffer(buffer));
|
||||
this.createDate = buffer.getLong();
|
||||
this.lastAccessDate = buffer.getLong();
|
||||
this.name = NodeDataUtils.getStringFromBuffer(buffer);
|
||||
this.displayName = NodeDataUtils.getStringFromBuffer(buffer);
|
||||
this.deletedItemFlags = buffer.getShort();
|
||||
}
|
||||
|
||||
} catch (BufferUnderflowException ex) {
|
||||
throw new InvalidDataException("Node data is incomplete", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the node data version number of this node.
|
||||
*
|
||||
* @return The version number.
|
||||
*/
|
||||
public int getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether or not any errors occurred during the processing of the job.
|
||||
* Gets whether or not any errors occurred during the processing of any auto
|
||||
* ingest job for the case represented by this node data.
|
||||
*
|
||||
* @return True or false.
|
||||
*/
|
||||
@ -89,7 +151,8 @@ public final class CaseNodeData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not any errors occurred during the processing of job.
|
||||
* Sets whether or not any errors occurred during the processing of any auto
|
||||
* ingest job for the case represented by this node data.
|
||||
*
|
||||
* @param errorsOccurred True or false.
|
||||
*/
|
||||
@ -98,12 +161,97 @@ public final class CaseNodeData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the node data version number.
|
||||
* Gets the path of the case directory of the case represented by this node
|
||||
* data.
|
||||
*
|
||||
* @return The version number.
|
||||
* @return The case directory path.
|
||||
*/
|
||||
public int getVersion() {
|
||||
return this.version;
|
||||
public Path getDirectory() {
|
||||
return this.directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path of the case directory of the case represented by this node
|
||||
* data.
|
||||
*
|
||||
* @param caseDirectory The case directory path.
|
||||
*/
|
||||
public void setDirectory(Path caseDirectory) {
|
||||
this.directory = caseDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date the case represented by this node data was created.
|
||||
*
|
||||
* @return The create date.
|
||||
*/
|
||||
public Date getCreateDate() {
|
||||
return new Date(this.createDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date the case represented by this node data was created.
|
||||
*
|
||||
* @param createDate The create date.
|
||||
*/
|
||||
public void setCreateDate(Date createDate) {
|
||||
this.createDate = createDate.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date the case represented by this node data last accessed.
|
||||
*
|
||||
* @return The last access date.
|
||||
*/
|
||||
public Date getLastAccessDate() {
|
||||
return new Date(this.lastAccessDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date the case represented by this node data was last accessed.
|
||||
*
|
||||
* @param lastAccessDate The last access date.
|
||||
*/
|
||||
public void setLastAccessDate(Date lastAccessDate) {
|
||||
this.lastAccessDate = lastAccessDate.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique and immutable (user cannot change it) name of the case
|
||||
* represented by this node data.
|
||||
*
|
||||
* @return The case name.
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the unique and immutable (user cannot change it) name of the case
|
||||
* represented by this node data.
|
||||
*
|
||||
* @param name The case name.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display name of the case represented by this node data.
|
||||
*
|
||||
* @return The case display name.
|
||||
*/
|
||||
public String getDisplayName() {
|
||||
return this.displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the display name of the case represented by this node data.
|
||||
*
|
||||
* @param displayName The case display name.
|
||||
*/
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,16 +261,31 @@ public final class CaseNodeData {
|
||||
* @return The node data as a byte array.
|
||||
*/
|
||||
public byte[] toArray() {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(5);
|
||||
int bufferSize = Integer.BYTES; // version
|
||||
bufferSize += 1; // errorsOccurred
|
||||
bufferSize += this.directory.toString().getBytes().length; // directory
|
||||
bufferSize += Long.BYTES; // createDate
|
||||
bufferSize += Long.BYTES; // lastAccessDate
|
||||
bufferSize += this.name.getBytes().length; // name
|
||||
bufferSize += this.displayName.getBytes().length; // displayName
|
||||
bufferSize += Short.BYTES; // deletedItemFlags
|
||||
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
|
||||
|
||||
buffer.putInt(this.version);
|
||||
buffer.put((byte)(this.errorsOccurred ? 0x80 : 0));
|
||||
buffer.put((byte) (this.errorsOccurred ? 0x80 : 0));
|
||||
|
||||
if (this.version >= 1) {
|
||||
NodeDataUtils.putStringIntoBuffer(this.directory.toString(), buffer);
|
||||
buffer.putLong(this.createDate);
|
||||
buffer.putLong(this.lastAccessDate);
|
||||
NodeDataUtils.putStringIntoBuffer(name, buffer);
|
||||
NodeDataUtils.putStringIntoBuffer(displayName, buffer);
|
||||
buffer.putShort(deletedItemFlags);
|
||||
}
|
||||
|
||||
// Prepare the array
|
||||
byte[] array = new byte[buffer.position()];
|
||||
buffer.rewind();
|
||||
buffer.get(array, 0, array.length);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.coordinationservice;
|
||||
|
||||
/**
|
||||
* Exception thrown when invlaid coordniation service node data is encountered.
|
||||
*/
|
||||
public final class InvalidNodeDataException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private InvalidNodeDataException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
private InvalidNodeDataException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
64
Core/src/org/sleuthkit/autopsy/coordinationservice/NodeDataUtils.java
Executable file
64
Core/src/org/sleuthkit/autopsy/coordinationservice/NodeDataUtils.java
Executable file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.coordinationservice;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
|
||||
/**
|
||||
* Utilities for reading and writing node data.
|
||||
*/
|
||||
public final class NodeDataUtils {
|
||||
|
||||
/**
|
||||
* This method retrieves a string from a given buffer. Assumes the string
|
||||
* was written using putStringIntoBuffer and first reads the the length of
|
||||
* the string.
|
||||
*
|
||||
* @param buffer The buffer from which the string will be read.
|
||||
*
|
||||
* @return The string read from the buffer.
|
||||
*/
|
||||
public static String getStringFromBuffer(ByteBuffer buffer) {
|
||||
String output = "";
|
||||
int length = buffer.getInt();
|
||||
if (length > 0) {
|
||||
byte[] array = new byte[length];
|
||||
buffer.get(array, 0, length);
|
||||
output = new String(array);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method puts a given string into a given buffer. The length of the
|
||||
* string will be inserted prior to the string.
|
||||
*
|
||||
* @param stringValue The string to write to the buffer.
|
||||
* @param buffer The buffer to which the string will be written.
|
||||
*/
|
||||
public static void putStringIntoBuffer(String stringValue, ByteBuffer buffer) {
|
||||
buffer.putInt(stringValue.getBytes().length);
|
||||
buffer.put(stringValue.getBytes());
|
||||
}
|
||||
|
||||
private NodeDataUtils() {
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user