From ccbb8aa64f08757d5ec3aff5bf3a8becff811ec8 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Fri, 28 Jun 2019 13:46:09 -0400 Subject: [PATCH 01/69] Fixed UI bug --- .../directorytree/ViewContextAction.java | 195 +++++++++++------- 1 file changed, 120 insertions(+), 75 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java index 8a92159ecb..c5ab6367f2 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java @@ -76,7 +76,7 @@ public class ViewContextAction extends AbstractAction { * parent of the content, selecting the parent in the tree view, then * selecting the content in the results view. * - * @param displayName The display name for the action. + * @param displayName The display name for the action. * @param artifactNode The artifact node for the artifact. */ public ViewContextAction(String displayName, BlackboardArtifactNode artifactNode) { @@ -98,21 +98,20 @@ public class ViewContextAction extends AbstractAction { * parent of the content, selecting the parent in the tree view, then * selecting the content in the results view. * - * @param displayName The display name for the action. + * @param displayName The display name for the action. * @param fileSystemContentNode The file system content node for the - * content. + * content. */ public ViewContextAction(String displayName, AbstractFsContentNode fileSystemContentNode) { super(displayName); this.content = fileSystemContentNode.getLookup().lookup(Content.class); } - - + /** - * An action that displays the context for abstract file by - * expanding the data sources branch of the tree view to the level of the - * parent of the content, selecting the parent in the tree view, then - * selecting the content in the results view. + * An action that displays the context for abstract file by expanding the + * data sources branch of the tree view to the level of the parent of the + * content, selecting the parent in the tree view, then selecting the + * content in the results view. * * @param displayName The display name for the action. * @param abstractAbstractFileNode The AbstractAbstractFileNode node for the @@ -121,7 +120,7 @@ public class ViewContextAction extends AbstractAction { public ViewContextAction(String displayName, AbstractAbstractFileNode abstractAbstractFileNode) { super(displayName); this.content = abstractAbstractFileNode.getLookup().lookup(Content.class); - } + } /** * An action that displays the context for some content by expanding the @@ -130,7 +129,7 @@ public class ViewContextAction extends AbstractAction { * content in the results view. * * @param displayName The display name for the action. - * @param content The content. + * @param content The content. */ public ViewContextAction(String displayName, Content content) { super(displayName); @@ -153,12 +152,29 @@ public class ViewContextAction extends AbstractAction { }) public void actionPerformed(ActionEvent event) { EventQueue.invokeLater(() -> { + + /* + * Get the parent content for the content to be selected in the + * results view. If the parent content is null, then the specified + * content is a data source, and the parent tree view node is the + * "Data Sources" node. Otherwise, the tree view needs to be + * searched to find the parent treeview node. + */ + Content parentContent = null; + try { + parentContent = content.getParent(); + } catch (TskCoreException ex) { + MessageNotifyUtil.Message.error(Bundle.ViewContextAction_errorMessage_cannotFindDirectory()); + logger.log(Level.SEVERE, String.format("Could not get parent of Content object: %s", content), ex); //NON-NLS + return; + } + /* * Get the "Data Sources" node from the tree view. */ DirectoryTreeTopComponent treeViewTopComponent = DirectoryTreeTopComponent.findInstance(); ExplorerManager treeViewExplorerMgr = treeViewTopComponent.getExplorerManager(); - Node parentTreeViewNode; + Node parentTreeViewNode = null; if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { // 'Group by Data Source' view SleuthkitCase skCase; @@ -169,13 +185,44 @@ public class ViewContextAction extends AbstractAction { long contentDSObjid = content.getDataSource().getId(); DataSource datasource = skCase.getDataSource(contentDSObjid); dsname = datasource.getName(); - Children rootChildren = treeViewExplorerMgr.getRootContext().getChildren(); - Node datasourceGroupingNode = rootChildren.findChild(dsname); - if (!Objects.isNull(datasourceGroupingNode)) { - Children dsChildren = datasourceGroupingNode.getChildren(); - parentTreeViewNode = dsChildren.findChild(DataSourcesNode.NAME); + + if (null != parentContent) { + // the tree view needs to be searched to find the parent treeview node. + /* NOTE: we can't do a lookup by data source name here, becase if there + are multiple data sources with the same name, then "getChildren().findChild(dsname)" + simply returns the first one that it finds. Instead we have to loop over all + data sources with that name, and make sure we find the correct one. + */ + for (int i = 0; i < rootChildren.getNodesCount(); i++) { + // in the root, look for a data source node with the name of interest + Node treeNode = rootChildren.getNodeAt(i); + if (!(treeNode.getName().equals(dsname))) { + continue; + } + + // for this data source, get the "Data Sources" child node + Node datasourceGroupingNode = treeNode.getChildren().findChild(DataSourcesNode.NAME); + + // check whether this is the data source we are looking for + parentTreeViewNode = findParentNodeInTree(parentContent, datasourceGroupingNode); + if (parentTreeViewNode != null) { + // found the data source node + break; + } + } } else { + /* If the parent content is null, then the specified + * content is a data source, and the parent tree view node is the + * "Data Sources" node. */ + Node datasourceGroupingNode = rootChildren.findChild(dsname); + if (!Objects.isNull(datasourceGroupingNode)) { + Children dsChildren = datasourceGroupingNode.getChildren(); + parentTreeViewNode = dsChildren.findChild(DataSourcesNode.NAME); + } + } + + if (parentTreeViewNode == null) { MessageNotifyUtil.Message.error(Bundle.ViewContextAction_errorMessage_cannotFindNode()); logger.log(Level.SEVERE, "Failed to locate data source node in tree."); //NON-NLS return; @@ -188,65 +235,12 @@ public class ViewContextAction extends AbstractAction { } else { // Classic view // Start the search at the DataSourcesNode parentTreeViewNode = treeViewExplorerMgr.getRootContext().getChildren().findChild(DataSourcesNode.NAME); - } - /* - * Get the parent content for the content to be selected in the - * results view. If the parent content is null, then the specified - * content is a data source, and the parent tree view node is the - * "Data Sources" node. Otherwise, the tree view needs to be - * searched to find the parent treeview node. - */ - Content parentContent = null; - - try { - parentContent = content.getParent(); - } catch (TskCoreException ex) { - MessageNotifyUtil.Message.error(Bundle.ViewContextAction_errorMessage_cannotFindDirectory()); - logger.log(Level.SEVERE, String.format("Could not get parent of Content object: %s", content), ex); //NON-NLS - return; - } - if (null != parentContent) { - /* - * Get an ordered list of the ancestors of the specified - * content, starting with its data source. - * - */ - AncestorVisitor ancestorVisitor = new AncestorVisitor(); - List contentBranch = parentContent.accept(ancestorVisitor); - Collections.reverse(contentBranch); - - /** - * Convert the list of ancestors into a list of tree nodes. - * - * IMPORTANT: The "dummy" root node used to create this single - * layer of children needs to be wrapped in a - * DirectoryTreeFilterNode so that its child nodes will also be - * wrapped in DirectoryTreeFilterNodes, via - * DirectoryTreeFilterNodeChildren. Otherwise, the display names - * of the nodes in the branch will not have child node counts - * and will not match the display names of the corresponding - * nodes in the actual tree view. - */ - Node dummyRootNode = new DirectoryTreeFilterNode(new AbstractNode(new RootContentChildren(contentBranch)), true); - Children ancestorChildren = dummyRootNode.getChildren(); - - /* - * Search the tree for the parent node. Note that this algorithm - * simply discards "extra" ancestor nodes not shown in the tree, - * such as the root directory of the file system for file system - * content. - */ - Children treeNodeChildren = parentTreeViewNode.getChildren(); - for (int i = 0; i < ancestorChildren.getNodesCount(); i++) { - Node ancestorNode = ancestorChildren.getNodeAt(i); - for (int j = 0; j < treeNodeChildren.getNodesCount(); j++) { - Node treeNode = treeNodeChildren.getNodeAt(j); - if (ancestorNode.getName().equals(treeNode.getName())) { - parentTreeViewNode = treeNode; - treeNodeChildren = treeNode.getChildren(); - break; - } + if (null != parentContent) { + // the tree view needs to be searched to find the parent treeview node. + Node potentialParentTreeViewNode = findParentNodeInTree(parentContent, parentTreeViewNode); + if (potentialParentTreeViewNode != null) { + parentTreeViewNode = potentialParentTreeViewNode; } } } @@ -292,6 +286,57 @@ public class ViewContextAction extends AbstractAction { }); } + /** + * Searches tree for parent node by getting an ordered list of the ancestors + * of the specified content. + * + * @param parentContent parent content for the content to be searched for + * @param node Node tree to search + * @return Node object of the matching parent, NULL if not found + */ + private Node findParentNodeInTree(Content parentContent, Node node) { + /* + * Get an ordered list of the ancestors of the specified + * content, starting with its data source. + * + */ + AncestorVisitor ancestorVisitor = new AncestorVisitor(); + List contentBranch = parentContent.accept(ancestorVisitor); + Collections.reverse(contentBranch); + + /** + * Convert the list of ancestors into a list of tree nodes. + * + * IMPORTANT: The "dummy" root node used to create this single layer of + * children needs to be wrapped in a DirectoryTreeFilterNode so that its + * child nodes will also be wrapped in DirectoryTreeFilterNodes, via + * DirectoryTreeFilterNodeChildren. Otherwise, the display names of the + * nodes in the branch will not have child node counts and will not + * match the display names of the corresponding nodes in the actual tree + * view. + */ + Node dummyRootNode = new DirectoryTreeFilterNode(new AbstractNode(new RootContentChildren(contentBranch)), true); + Children ancestorChildren = dummyRootNode.getChildren(); + + /* + * Search the tree for the parent node. Note that this algorithm + * simply discards "extra" ancestor nodes not shown in the tree, + * such as the root directory of the file system for file system + * content. + */ + Children treeNodeChildren = node.getChildren(); + for (int i = 0; i < ancestorChildren.getNodesCount(); i++) { + Node ancestorNode = ancestorChildren.getNodeAt(i); + for (int j = 0; j < treeNodeChildren.getNodesCount(); j++) { + Node treeNode = treeNodeChildren.getNodeAt(j); + if (ancestorNode.getName().equals(treeNode.getName())) { + return treeNode; + } + } + } + return null; + } + /** * A ContentVisitor that returns a list of content objects by starting with * a given content and following its chain of ancestors to the root content From 8ce95512d397d6f3508c0d7fa10f8e32a7d30d72 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 1 Jul 2019 09:46:53 -0400 Subject: [PATCH 02/69] 2173 disable multi-select on open recent case dialog --- .../org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java index 5f164cd00f..b2e0bbc70a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,6 +24,7 @@ import java.io.File; import java.util.logging.Level; import javax.swing.JOptionPane; import javax.swing.JTable; +import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.table.AbstractTableModel; import org.openide.util.NbBundle; @@ -51,6 +52,7 @@ class OpenRecentCasePanel extends javax.swing.JPanel { */ private OpenRecentCasePanel() { initComponents(); + imagesTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } /* From 3c3d6699c3cfb1e3c20a52236a2dda2a125edb66 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 1 Jul 2019 10:03:26 -0400 Subject: [PATCH 03/69] 2173 disable ok button when case is deselected --- .../autopsy/casemodule/OpenRecentCasePanel.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java index b2e0bbc70a..88c519c0cf 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java @@ -26,6 +26,8 @@ import javax.swing.JOptionPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; @@ -53,6 +55,15 @@ class OpenRecentCasePanel extends javax.swing.JPanel { private OpenRecentCasePanel() { initComponents(); imagesTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + imagesTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + //enable the ok button when something is selected + if (!e.getValueIsAdjusting()){ + openButton.setEnabled(e.getFirstIndex()>=0); + } + } + }); } /* @@ -90,11 +101,9 @@ class OpenRecentCasePanel extends javax.swing.JPanel { imagesTable.getColumnModel().getColumn(0).setPreferredWidth((int) (.30 * width)); imagesTable.getColumnModel().getColumn(1).setPreferredWidth((int) (.70 * width)); // If there are any images, let's select the first one + openButton.setEnabled(false); if (imagesTable.getRowCount() > 0) { imagesTable.setRowSelectionInterval(0, 0); - openButton.setEnabled(true); - } else { - openButton.setEnabled(false); } } From 94d833b56ffef88579f12fc40b587e6cdd1893bc Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 1 Jul 2019 10:11:38 -0400 Subject: [PATCH 04/69] 2173 fix selection listener for disabling / enabling ok button --- .../org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.form | 1 + .../org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.form index bb12ba69d9..5e9ec1f38a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.form @@ -76,6 +76,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java index 88c519c0cf..401663056b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java @@ -60,7 +60,7 @@ class OpenRecentCasePanel extends javax.swing.JPanel { public void valueChanged(ListSelectionEvent e) { //enable the ok button when something is selected if (!e.getValueIsAdjusting()){ - openButton.setEnabled(e.getFirstIndex()>=0); + openButton.setEnabled(imagesTable.getSelectedRowCount() > 0); } } }); @@ -101,7 +101,6 @@ class OpenRecentCasePanel extends javax.swing.JPanel { imagesTable.getColumnModel().getColumn(0).setPreferredWidth((int) (.30 * width)); imagesTable.getColumnModel().getColumn(1).setPreferredWidth((int) (.70 * width)); // If there are any images, let's select the first one - openButton.setEnabled(false); if (imagesTable.getRowCount() > 0) { imagesTable.setRowSelectionInterval(0, 0); } @@ -262,6 +261,7 @@ class OpenRecentCasePanel extends javax.swing.JPanel { cancelButton.setText(org.openide.util.NbBundle.getMessage(OpenRecentCasePanel.class, "OpenRecentCasePanel.cancelButton.text")); // NOI18N openButton.setText(org.openide.util.NbBundle.getMessage(OpenRecentCasePanel.class, "OpenRecentCasePanel.openButton.text")); // NOI18N + openButton.setEnabled(false); openButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { openButtonActionPerformed(evt); From e65005dc4c821462ce42dd4deb0cb8be868c9b10 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 1 Jul 2019 10:29:14 -0400 Subject: [PATCH 05/69] 3622 disable resizing of manage correlation attrs dialog, raise min size of org dialog --- .../ManageCorrelationPropertiesDialog.form | 11 ++++++----- .../ManageCorrelationPropertiesDialog.java | 5 +++-- .../optionspanel/ManageOrganizationsDialog.form | 6 +++--- .../optionspanel/ManageOrganizationsDialog.java | 6 +++--- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.form b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.form index 5124d1d85d..a3ab756f64 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.form @@ -7,6 +7,7 @@ + @@ -49,16 +50,16 @@ - - - + + + - + - + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.java index 550244dffe..883efff72b 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.java @@ -132,6 +132,7 @@ final class ManageCorrelationPropertiesDialog extends javax.swing.JDialog { taInstructions = new javax.swing.JTextArea(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setResizable(false); org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(ManageCorrelationPropertiesDialog.class, "ManageCorrelationPropertiesDialog.okButton.text")); // NOI18N okButton.addActionListener(new java.awt.event.ActionListener() { @@ -220,14 +221,14 @@ final class ManageCorrelationPropertiesDialog extends javax.swing.JDialog { .addGap(20, 20, 20) .addComponent(taInstructions, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 254, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(lbWarningMsg, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(okButton) .addComponent(cancelButton)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) ); pack(); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.form b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.form index 7bccab25f6..734229d4ed 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.form @@ -3,7 +3,7 @@
- + @@ -27,7 +27,7 @@ - + @@ -35,7 +35,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java index 26a63f03a4..cfd4b38d7a 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java @@ -159,7 +159,7 @@ public final class ManageOrganizationsDialog extends JDialog { editButton = new javax.swing.JButton(); orgDetailsLabel = new javax.swing.JLabel(); - setMinimumSize(new java.awt.Dimension(545, 415)); + setMinimumSize(new java.awt.Dimension(603, 443)); manageOrganizationsScrollPane.setMinimumSize(null); manageOrganizationsScrollPane.setPreferredSize(new java.awt.Dimension(535, 415)); @@ -331,13 +331,13 @@ public final class ManageOrganizationsDialog extends JDialog { layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGap(0, 0, 0) - .addComponent(manageOrganizationsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(manageOrganizationsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 603, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGap(0, 0, 0) - .addComponent(manageOrganizationsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(manageOrganizationsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 443, Short.MAX_VALUE)) ); pack(); From ba22236b3018701d02f3b73b24a06a0454552ebd Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 1 Jul 2019 10:44:43 -0400 Subject: [PATCH 06/69] 3622 tweak min size for manage orgs dialog --- .../optionspanel/ManageOrganizationsDialog.form | 5 ++++- .../optionspanel/ManageOrganizationsDialog.java | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.form b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.form index 734229d4ed..33c03ebf9d 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.form @@ -3,7 +3,10 @@ - + + + + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java index cfd4b38d7a..b36b3de413 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java @@ -159,7 +159,8 @@ public final class ManageOrganizationsDialog extends JDialog { editButton = new javax.swing.JButton(); orgDetailsLabel = new javax.swing.JLabel(); - setMinimumSize(new java.awt.Dimension(603, 443)); + setMinimumSize(new java.awt.Dimension(600, 450)); + setPreferredSize(new java.awt.Dimension(600, 450)); manageOrganizationsScrollPane.setMinimumSize(null); manageOrganizationsScrollPane.setPreferredSize(new java.awt.Dimension(535, 415)); From c5e185a32ce61875fbfe11d44055158b735c149f Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 1 Jul 2019 13:48:58 -0400 Subject: [PATCH 07/69] 3617 make CR config dialog better handle very paths to sql lite repos --- .../optionspanel/Bundle.properties | 3 +- .../optionspanel/Bundle.properties-MERGED | 1 - .../optionspanel/EamDbSettingsDialog.form | 181 +++++++++++++----- .../optionspanel/EamDbSettingsDialog.java | 130 ++++++++----- 4 files changed, 216 insertions(+), 99 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties index 635f5a558c..0b30e2584f 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties @@ -57,7 +57,6 @@ ManageCorrelationPropertiesDialog.cancelButton.text=Cancel ManageCorrelationPropertiesDialog.okButton.text=OK GlobalSettingsPanel.bnManageProperties.text=Manage Correlation Properties EamDbSettingsDialog.lbDatabaseDesc.text=Database File: -EamDbSettingsDialog.lbFullDbPath.text= GlobalSettingsPanel.cbUseCentralRepo.text=Use a Central Repository GlobalSettingsPanel.organizationTextArea.text=Organization information can be tracked in the Central Repository. GlobalSettingsPanel.manageOrganizationButton.text=Manage Organizations @@ -84,4 +83,4 @@ ManageCasesDialog.orgLabel.text=Organization: ManageCasesDialog.closeButton.text=Close ManageCasesDialog.notesLabel.text=Notes: ManageCasesDialog.dataSourcesLabel.text=Data Sources: -ManageCasesDialog.caseInfoLabel.text=Case Info: \ No newline at end of file +ManageCasesDialog.caseInfoLabel.text=Case Info: diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED index 454cb29e39..dc42aa3b68 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED @@ -115,7 +115,6 @@ ManageCorrelationPropertiesDialog.cancelButton.text=Cancel ManageCorrelationPropertiesDialog.okButton.text=OK GlobalSettingsPanel.bnManageProperties.text=Manage Correlation Properties EamDbSettingsDialog.lbDatabaseDesc.text=Database File: -EamDbSettingsDialog.lbFullDbPath.text= GlobalSettingsPanel.cbUseCentralRepo.text=Use a Central Repository GlobalSettingsPanel.organizationTextArea.text=Organization information can be tracked in the Central Repository. GlobalSettingsPanel.manageOrganizationButton.text=Manage Organizations diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.form b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.form index 50979ba938..b063773f48 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.form @@ -9,6 +9,7 @@ + @@ -35,18 +36,18 @@ - + - - - + + + - + @@ -59,9 +60,9 @@ - + - + @@ -85,6 +86,15 @@ + + + + + + + + + @@ -115,43 +125,37 @@ - + - - + + + - + - - - - - - - + + + + - - - - - - - - - - - - - + + + + + + + + + @@ -171,40 +175,42 @@ - + - + - + - + - + - + - - - - + + + + + + + + - - @@ -215,6 +221,9 @@ + + + @@ -225,6 +234,9 @@ + + + @@ -242,42 +254,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -291,6 +338,9 @@ + + + @@ -298,6 +348,15 @@ + + + + + + + + + @@ -305,12 +364,8 @@ - - - - - - + + @@ -324,6 +379,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java index 19b1c4c8e5..5c68b51b92 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java @@ -142,12 +142,17 @@ public class EamDbSettingsDialog extends JDialog { lbSingleUserSqLite = new javax.swing.JLabel(); lbDatabaseType = new javax.swing.JLabel(); lbDatabaseDesc = new javax.swing.JLabel(); - lbFullDbPath = new javax.swing.JLabel(); filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)); + dataBaseFileScrollPane = new javax.swing.JScrollPane(); + dataBaseFileTextArea = new javax.swing.JTextArea(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setResizable(false); org.openide.awt.Mnemonics.setLocalizedText(bnCancel, org.openide.util.NbBundle.getMessage(EamDbSettingsDialog.class, "EamDbSettingsDialog.bnCancel.text")); // NOI18N + bnCancel.setMaximumSize(new java.awt.Dimension(79, 23)); + bnCancel.setMinimumSize(new java.awt.Dimension(79, 23)); + bnCancel.setPreferredSize(new java.awt.Dimension(79, 23)); bnCancel.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { bnCancelActionPerformed(evt); @@ -169,25 +174,30 @@ public class EamDbSettingsDialog extends JDialog { .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(bnOk) .addGap(11, 11, 11) - .addComponent(bnCancel) + .addComponent(bnCancel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) ); + + pnButtonsLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {bnCancel, bnOk}); + pnButtonsLayout.setVerticalGroup( pnButtonsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(pnButtonsLayout.createSequentialGroup() .addGap(0, 0, 0) .addGroup(pnButtonsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(bnOk) - .addComponent(bnCancel)) + .addComponent(bnCancel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGap(0, 0, 0)) ); pnSQLiteSettings.setBorder(javax.swing.BorderFactory.createEtchedBorder()); org.openide.awt.Mnemonics.setLocalizedText(lbDatabasePath, org.openide.util.NbBundle.getMessage(EamDbSettingsDialog.class, "EamDbSettingsDialog.lbDatabasePath.text")); // NOI18N + lbDatabasePath.setPreferredSize(new java.awt.Dimension(80, 14)); tfDatabasePath.setText(org.openide.util.NbBundle.getMessage(EamDbSettingsDialog.class, "EamDbSettingsDialog.tfDatabasePath.text")); // NOI18N tfDatabasePath.setToolTipText(org.openide.util.NbBundle.getMessage(EamDbSettingsDialog.class, "EamDbSettingsDialog.tfDatabasePath.toolTipText")); // NOI18N + tfDatabasePath.setPreferredSize(new java.awt.Dimension(420, 23)); org.openide.awt.Mnemonics.setLocalizedText(bnDatabasePathFileOpen, org.openide.util.NbBundle.getMessage(EamDbSettingsDialog.class, "EamDbSettingsDialog.bnDatabasePathFileOpen.text")); // NOI18N bnDatabasePathFileOpen.addActionListener(new java.awt.event.ActionListener() { @@ -197,14 +207,27 @@ public class EamDbSettingsDialog extends JDialog { }); org.openide.awt.Mnemonics.setLocalizedText(lbHostName, org.openide.util.NbBundle.getMessage(EamDbSettingsDialog.class, "EamDbSettingsDialog.lbHostName.text")); // NOI18N + lbHostName.setPreferredSize(new java.awt.Dimension(80, 14)); + + tbDbHostname.setPreferredSize(new java.awt.Dimension(509, 20)); org.openide.awt.Mnemonics.setLocalizedText(lbPort, org.openide.util.NbBundle.getMessage(EamDbSettingsDialog.class, "EamDbSettingsDialog.lbPort.text")); // NOI18N + lbPort.setPreferredSize(new java.awt.Dimension(80, 14)); + + tbDbPort.setPreferredSize(new java.awt.Dimension(509, 20)); org.openide.awt.Mnemonics.setLocalizedText(lbUserName, org.openide.util.NbBundle.getMessage(EamDbSettingsDialog.class, "EamDbSettingsDialog.lbUserName.text")); // NOI18N + lbUserName.setPreferredSize(new java.awt.Dimension(80, 14)); + + tbDbUsername.setPreferredSize(new java.awt.Dimension(509, 20)); org.openide.awt.Mnemonics.setLocalizedText(lbUserPassword, org.openide.util.NbBundle.getMessage(EamDbSettingsDialog.class, "EamDbSettingsDialog.lbUserPassword.text")); // NOI18N + lbUserPassword.setPreferredSize(new java.awt.Dimension(80, 14)); + + jpDbPassword.setPreferredSize(new java.awt.Dimension(509, 20)); cbDatabaseType.setModel(new javax.swing.DefaultComboBoxModel<>(new EamDbPlatformEnum[]{EamDbPlatformEnum.POSTGRESQL, EamDbPlatformEnum.SQLITE})); + cbDatabaseType.setPreferredSize(new java.awt.Dimension(120, 20)); cbDatabaseType.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { cbDatabaseTypeActionPerformed(evt); @@ -212,12 +235,25 @@ public class EamDbSettingsDialog extends JDialog { }); org.openide.awt.Mnemonics.setLocalizedText(lbSingleUserSqLite, org.openide.util.NbBundle.getMessage(EamDbSettingsDialog.class, "EamDbSettingsDialog.lbSingleUserSqLite.text")); // NOI18N + lbSingleUserSqLite.setPreferredSize(new java.awt.Dimension(381, 14)); org.openide.awt.Mnemonics.setLocalizedText(lbDatabaseType, org.openide.util.NbBundle.getMessage(EamDbSettingsDialog.class, "EamDbSettingsDialog.lbDatabaseType.text")); // NOI18N + lbDatabaseType.setMaximumSize(new java.awt.Dimension(80, 14)); + lbDatabaseType.setMinimumSize(new java.awt.Dimension(80, 14)); + lbDatabaseType.setPreferredSize(new java.awt.Dimension(80, 14)); org.openide.awt.Mnemonics.setLocalizedText(lbDatabaseDesc, org.openide.util.NbBundle.getMessage(EamDbSettingsDialog.class, "EamDbSettingsDialog.lbDatabaseDesc.text")); // NOI18N + lbDatabaseDesc.setPreferredSize(new java.awt.Dimension(80, 14)); - org.openide.awt.Mnemonics.setLocalizedText(lbFullDbPath, org.openide.util.NbBundle.getMessage(EamDbSettingsDialog.class, "EamDbSettingsDialog.lbFullDbPath.text")); // NOI18N + dataBaseFileScrollPane.setBorder(null); + + dataBaseFileTextArea.setEditable(false); + dataBaseFileTextArea.setBackground(new java.awt.Color(240, 240, 240)); + dataBaseFileTextArea.setColumns(20); + dataBaseFileTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N + dataBaseFileTextArea.setLineWrap(true); + dataBaseFileTextArea.setRows(3); + dataBaseFileScrollPane.setViewportView(dataBaseFileTextArea); javax.swing.GroupLayout pnSQLiteSettingsLayout = new javax.swing.GroupLayout(pnSQLiteSettings); pnSQLiteSettings.setLayout(pnSQLiteSettingsLayout); @@ -226,34 +262,30 @@ public class EamDbSettingsDialog extends JDialog { .addGroup(pnSQLiteSettingsLayout.createSequentialGroup() .addContainerGap() .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(lbHostName) - .addComponent(lbPort) - .addComponent(lbUserName) - .addComponent(lbDatabaseType) + .addComponent(lbHostName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbDatabaseType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbDatabasePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbUserName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(lbDatabasePath, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(lbUserPassword, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addComponent(lbDatabaseDesc)) + .addComponent(lbDatabaseDesc, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(lbUserPassword, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) .addGap(10, 10, 10) .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(lbFullDbPath, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(pnSQLiteSettingsLayout.createSequentialGroup() + .addComponent(tfDatabasePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(bnDatabasePathFileOpen)) .addGroup(pnSQLiteSettingsLayout.createSequentialGroup() .addComponent(cbDatabaseType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lbSingleUserSqLite, javax.swing.GroupLayout.DEFAULT_SIZE, 467, Short.MAX_VALUE) - .addGap(9, 9, 9)) - .addGroup(pnSQLiteSettingsLayout.createSequentialGroup() - .addComponent(tfDatabasePath) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(bnDatabasePathFileOpen) - .addGap(11, 11, 11)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnSQLiteSettingsLayout.createSequentialGroup() - .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(tbDbHostname, javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jpDbPassword, javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(tbDbUsername) - .addComponent(tbDbPort, javax.swing.GroupLayout.Alignment.LEADING)) - .addGap(10, 10, 10)))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(lbSingleUserSqLite, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(jpDbPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(tbDbUsername, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(tbDbPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(tbDbHostname, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(dataBaseFileScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 509, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap()) .addGroup(pnSQLiteSettingsLayout.createSequentialGroup() .addGap(55, 55, 55) .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -266,35 +298,36 @@ public class EamDbSettingsDialog extends JDialog { .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(cbDatabaseType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lbSingleUserSqLite)) - .addComponent(lbDatabaseType, javax.swing.GroupLayout.Alignment.TRAILING)) + .addComponent(lbSingleUserSqLite, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(lbDatabaseType, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lbDatabasePath) - .addComponent(tfDatabasePath, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbDatabasePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(tfDatabasePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(bnDatabasePathFileOpen)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(tbDbHostname, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lbHostName)) + .addComponent(lbHostName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(tbDbPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lbPort)) + .addComponent(lbPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(tbDbUsername, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lbUserName)) + .addComponent(lbUserName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jpDbPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lbUserPassword)) + .addComponent(lbUserPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lbFullDbPath, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(lbDatabaseDesc, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnSQLiteSettingsLayout.createSequentialGroup() + .addComponent(lbDatabaseDesc, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18) + .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(dataBaseFileScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap()) ); @@ -312,11 +345,11 @@ public class EamDbSettingsDialog extends JDialog { layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addGap(10, 10, 10) - .addComponent(pnSQLiteSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 11, Short.MAX_VALUE) + .addContainerGap() + .addComponent(pnSQLiteSettings, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(pnButtons, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(10, 10, 10)) + .addContainerGap()) ); pack(); @@ -357,6 +390,7 @@ public class EamDbSettingsDialog extends JDialog { } try { tfDatabasePath.setText(databaseFile.getCanonicalPath()); + tfDatabasePath.setCaretPosition(tfDatabasePath.getText().length()); valid(); } catch (IOException ex) { logger.log(Level.SEVERE, "Failed to get path of selected database file", ex); // NON-NLS @@ -584,14 +618,15 @@ public class EamDbSettingsDialog extends JDialog { }//GEN-LAST:event_cbDatabaseTypeActionPerformed private void updateFullDbPath() { - lbFullDbPath.setText(tfDatabasePath.getText() + File.separator + CENTRAL_REPO_DB_NAME + CENTRAL_REPO_SQLITE_EXT); + dataBaseFileTextArea.setText(tfDatabasePath.getText() + File.separator + CENTRAL_REPO_DB_NAME + CENTRAL_REPO_SQLITE_EXT); + dataBaseFileTextArea.setCaretPosition(dataBaseFileTextArea.getText().length()); } private void displayDatabaseSettings(boolean isPostgres) { lbDatabasePath.setVisible(!isPostgres); tfDatabasePath.setVisible(!isPostgres); lbDatabaseDesc.setVisible(!isPostgres); - lbFullDbPath.setVisible(!isPostgres); + dataBaseFileTextArea.setVisible(!isPostgres); lbSingleUserSqLite.setVisible(!isPostgres); bnDatabasePathFileOpen.setVisible(!isPostgres); lbHostName.setVisible(isPostgres); @@ -855,13 +890,14 @@ public class EamDbSettingsDialog extends JDialog { private javax.swing.ButtonGroup bnGrpDatabasePlatforms; private javax.swing.JButton bnOk; private javax.swing.JComboBox cbDatabaseType; + private javax.swing.JScrollPane dataBaseFileScrollPane; + private javax.swing.JTextArea dataBaseFileTextArea; private javax.swing.JFileChooser fcDatabasePath; private javax.swing.Box.Filler filler1; private javax.swing.JPasswordField jpDbPassword; private javax.swing.JLabel lbDatabaseDesc; private javax.swing.JLabel lbDatabasePath; private javax.swing.JLabel lbDatabaseType; - private javax.swing.JLabel lbFullDbPath; private javax.swing.JLabel lbHostName; private javax.swing.JLabel lbPort; private javax.swing.JLabel lbSingleUserSqLite; From f05cb9590b0bb600c57485e3ed3f28dade8a76ee Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Tue, 2 Jul 2019 15:17:45 -0400 Subject: [PATCH 08/69] Reduced the memory foot print of the email parser --- thunderbirdparser/ivy.xml | 1 + .../nbproject/project.properties | 6 + thunderbirdparser/nbproject/project.xml | 28 +- .../autopsy/thunderbirdparser/MboxParser.java | 202 +++++++--- .../autopsy/thunderbirdparser/PstParser.java | 346 ++++++++++++++---- .../ThunderbirdMboxFileIngestModule.java | 144 +++++--- 6 files changed, 541 insertions(+), 186 deletions(-) diff --git a/thunderbirdparser/ivy.xml b/thunderbirdparser/ivy.xml index b66def4489..d2833c64c1 100644 --- a/thunderbirdparser/ivy.xml +++ b/thunderbirdparser/ivy.xml @@ -13,5 +13,6 @@ + diff --git a/thunderbirdparser/nbproject/project.properties b/thunderbirdparser/nbproject/project.properties index af5e0794ae..ea9d0786eb 100644 --- a/thunderbirdparser/nbproject/project.properties +++ b/thunderbirdparser/nbproject/project.properties @@ -1,13 +1,19 @@ +file.reference.apache-mime4j-core-0.8.0-SNAPSHOT.jar=release/modules/ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar +file.reference.apache-mime4j-dom-0.8.0-SNAPSHOT.jar=release/modules/ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar +file.reference.apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar=release/modules/ext/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar file.reference.commons-lang3-3.8.1.jar=release/modules/ext/commons-lang3-3.8.1.jar file.reference.apache-mime4j-core-0.8.0.jar=release/modules/ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar file.reference.apache-mime4j-dom-0.8.0.jar=release/modules/ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar file.reference.apache-mime4j-mbox-iterator-0.8.0.jar=release/modules/ext/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar +file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar file.reference.java-libpst-1.0-SNAPSHOT.jar=release/modules/ext/java-libpst-1.0-SNAPSHOT.jar file.reference.ez-vcard-0.10.5.jar=release/modules/ext/ez-vcard-0.10.5.jar file.reference.vinnie-2.0.2.jar=release/modules/ext/vinnie-2.0.2.jar javac.source=1.8 javac.compilerargs=-Xlint -Xlint:-serial +javadoc.reference.guava-19.0.jar=release/modules/ext/guava-19.0-javadoc.jar license.file=../LICENSE-2.0.txt nbm.homepage=http://www.sleuthkit.org/autopsy/ nbm.needs.restart=true +source.reference.guava-19.0.jar=release/modules/ext/guava-19.0-sources.jar spec.version.base=4.0 diff --git a/thunderbirdparser/nbproject/project.xml b/thunderbirdparser/nbproject/project.xml index d6e024f08c..c67ed7679f 100644 --- a/thunderbirdparser/nbproject/project.xml +++ b/thunderbirdparser/nbproject/project.xml @@ -54,18 +54,6 @@ ext/commons-lang3-3.8.1.jar release/modules/ext/commons-lang3-3.8.1.jar - - ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar - release/modules/ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar - - - ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar - release/modules/ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar - - - ext/java-libpst-1.0-SNAPSHOT.jar - release/modules/ext/java-libpst-1.0-SNAPSHOT.jar - ext/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar release/modules/ext/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar @@ -74,6 +62,22 @@ ext/ez-vcard-0.10.5.jar release/modules/ext/ez-vcard-0.10.5.jar + + ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar + release/modules/ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar + + + ext/java-libpst-1.0-SNAPSHOT.jar + release/modules/ext/java-libpst-1.0-SNAPSHOT.jar + + + ext/guava-19.0.jar + release/modules/ext/guava-19.0.jar + + + ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar + release/modules/ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar + ext/vinnie-2.0.2.jar release/modules/ext/vinnie-2.0.2.jar diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java index 42d74bcc2a..6604c2c623 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,6 +33,7 @@ import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.UUID; import java.util.logging.Level; @@ -57,22 +58,17 @@ import org.apache.tika.parser.txt.CharsetDetector; import org.apache.tika.parser.txt.CharsetMatch; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.EncodedFileOutputStream; /** - * A parser that extracts information about email messages and attachments from - * a mbox file. - * - * @author jwallace + * An Iterator for parsing mbox files. Wraps an instance of MBoxEmailIterator. */ -class MboxParser { +class MboxParser implements Iterator{ private static final Logger logger = Logger.getLogger(MboxParser.class.getName()); - private DefaultMessageBuilder messageBuilder; - private IngestServices services; - private StringBuilder errors; + private final DefaultMessageBuilder messageBuilder; + private final StringBuilder errors; /** * The mime type string for html text. @@ -83,9 +79,11 @@ class MboxParser { * The local path of the mbox file. */ private String localPath; + + private Iterator emailIterator = null; + - MboxParser(IngestServices services, String localPath) { - this.services = services; + private MboxParser( String localPath) { this.localPath = localPath; messageBuilder = new DefaultMessageBuilder(); MimeConfig config = MimeConfig.custom().setMaxLineLen(-1).build(); @@ -97,63 +95,76 @@ class MboxParser { static boolean isValidMimeTypeMbox(byte[] buffer) { return (new String(buffer)).startsWith("From "); //NON-NLS } - + /** - * Parse the mbox file and get the email messages. - * - * @param mboxFile - * - * @return a list of the email messages in the mbox file. + * Returns an instance of MBoxParser that will iterate and return EMailMessage + * objects with only the information needed for threading emails. + * + * @param localPath String path to the mboxFile + * @param mboxFile The mboxFile to parse + * @return Instance of MboxParser */ - List parse(File mboxFile, long fileID) { + static MboxParser getThreadInfoIterator(String localPath, File mboxFile) { + MboxParser parser = new MboxParser(localPath); + parser.createIterator(mboxFile, 0, false); + return parser; + } + + /** + * Returns an instance of MBoxParser that will iterate "whole" EmailMessages. + * + * @param localPath String path to the mboxFile + * @param mboxFile The mboxFile to parse + * @param fileID The fileID of the abstractFile that mboxFile was found + * @return Instance of MboxParser + */ + static MboxParser getEmailIterator(String localPath, File mboxFile, long fileID) { + MboxParser parser = new MboxParser(localPath); + parser.createIterator(mboxFile, fileID, true); + + return parser; + } + + /** + * Creates the real Iterator object instance. + * + * @param mboxFile The mboxFile to parse + * @param fileID The fileID of the abstractFile that mboxFile was found + * @param wholeMsg True if EmailMessage should have the whole message, + * not just the thread information. + */ + private void createIterator(File mboxFile, long fileID, boolean wholeMsg) { // Detect possible charsets List encoders = getPossibleEncoders(mboxFile); - - CharsetEncoder theEncoder = null; - Iterable mboxIterator = null; + // Loop through the possible encoders and find the first one that works. // That will usually be one of the first ones. for (CharsetEncoder encoder : encoders) { try { - mboxIterator = MboxIterator.fromFile(mboxFile).charset(encoder.charset()).build(); - theEncoder = encoder; + Iterable mboxIterable = MboxIterator.fromFile(mboxFile).charset(encoder.charset()).build(); + if(mboxIterable != null) { + emailIterator = new MBoxEmailIterator(mboxIterable.iterator(), encoder, fileID, wholeMsg); + } break; } catch (CharConversionException | UnsupportedCharsetException ex) { // Not the right encoder } catch (IllegalArgumentException ex) { // Not the right encoder } catch (IOException ex) { - logger.log(Level.WARNING, "couldn't find mbox file.", ex); //NON-NLS + logger.log(Level.WARNING, String.format("Failed to open mbox file: %s %d", mboxFile.getName(), fileID), ex); //NON-NLS addErrorMessage(NbBundle.getMessage(this.getClass(), "MboxParser.parse.errMsg.failedToReadFile")); - return new ArrayList<>(); } } - - // If no encoders work, post an error message and return. - if (mboxIterator == null || theEncoder == null) { - addErrorMessage(NbBundle.getMessage(this.getClass(), "MboxParser.parse.errMsg.couldntFindCharset")); - return new ArrayList<>(); - } - - List emails = new ArrayList<>(); - long failCount = 0; - - // Parse each message and extract an EmailMessage structure - for (CharBufferWrapper message : mboxIterator) { - try { - Message msg = messageBuilder.parseMessage(message.asInputStream(theEncoder.charset())); - emails.add(extractEmail(msg, fileID)); - } catch (RuntimeException | IOException ex) { - logger.log(Level.WARNING, "Failed to get message from mbox: {0}", ex.getMessage()); //NON-NLS - failCount++; - } - } - - if (failCount > 0) { - addErrorMessage( - NbBundle.getMessage(this.getClass(), "MboxParser.parse.errMsg.failedToParseNMsgs", failCount)); - } - return emails; + } + + @Override + public boolean hasNext() { + return emailIterator != null ? emailIterator.hasNext() : false; + } + + @Override + public EmailMessage next() { + return emailIterator != null ? emailIterator.next() : null; } String getErrors() { @@ -211,6 +222,44 @@ class MboxParser { return email; } + + /** + * Extract the subject, inReplyTo, message-ID and references from the Message + * object and returns them in a new EmailMessage object. + * + * @param msg Message object + * + * @return EmailMessage instance with only some of the message information + */ + private EmailMessage extractPartialEmail(Message msg) { + EmailMessage email = new EmailMessage(); + email.setSubject(msg.getSubject()); + email.setMessageID(msg.getMessageId()); + + Field field = msg.getHeader().getField("in-reply-to"); //NON-NLS + String inReplyTo = null; + + if (field != null) { + inReplyTo = field.getBody(); + email.setInReplyToID(inReplyTo); + } + + field = msg.getHeader().getField("references"); + if (field != null) { + List references = new ArrayList<>(); + for (String id : field.getBody().split(">")) { + references.add(id.trim() + ">"); + } + + if (!references.contains(inReplyTo)) { + references.add(inReplyTo); + } + + email.setReferences(references); + } + + return email; + } /** * Handle a multipart mime message. Recursively calls handleMultipart if one @@ -333,7 +382,7 @@ class MboxParser { addErrorMessage( NbBundle.getMessage(this.getClass(), "MboxParser.handleAttch.errMsg.failedToCreateOnDisk", outPath)); - logger.log(Level.INFO, "Failed to create file output stream for: " + outPath, ex); //NON-NLS + logger.log(Level.WARNING, "Failed to create file output stream for: " + outPath, ex); //NON-NLS return; } @@ -346,14 +395,14 @@ class MboxParser { // This could potentially be other types. Only seen this once. } } catch (IOException ex) { - logger.log(Level.INFO, "Failed to write mbox email attachment to disk.", ex); //NON-NLS + logger.log(Level.WARNING, "Failed to write mbox email attachment to disk.", ex); //NON-NLS addErrorMessage(NbBundle.getMessage(this.getClass(), "MboxParser.handleAttch.failedWriteToDisk", filename)); return; } finally { try { fos.close(); } catch (IOException ex) { - logger.log(Level.INFO, "Failed to close file output stream", ex); //NON-NLS + logger.log(Level.WARNING, "Failed to close file output stream", ex); //NON-NLS } } @@ -441,7 +490,7 @@ class MboxParser { try { is.close(); } catch (IOException ex) { - logger.log(Level.INFO, "Failed to close input stream"); //NON-NLS + logger.log(Level.WARNING, "Failed to close input stream"); //NON-NLS } } } @@ -449,4 +498,45 @@ class MboxParser { private void addErrorMessage(String msg) { errors.append("
  • ").append(msg).append("
  • "); //NON-NLS } + + /** + * An Interator for mbox email messages. + */ + final class MBoxEmailIterator implements Iterator { + + private final Iterator mboxIterator; + private final CharsetEncoder encoder; + private final long fileID; + private final boolean wholeMsg; + + MBoxEmailIterator(Iterator mboxIter, CharsetEncoder encoder, long fileID, boolean wholeMsg) { + mboxIterator = mboxIter; + this.encoder = encoder; + this.fileID = fileID; + this.wholeMsg = wholeMsg; + } + + @Override + public boolean hasNext() { + return (mboxIterator != null && encoder != null) ? mboxIterator.hasNext() : false; + } + + @Override + public EmailMessage next() { + CharBufferWrapper messageBuffer = mboxIterator.next(); + + try { + Message msg = messageBuilder.parseMessage(messageBuffer.asInputStream(encoder.charset())); + if(wholeMsg) { + return extractEmail(msg, fileID); + } else { + return extractPartialEmail(msg); + } + } catch (RuntimeException | IOException ex) { + logger.log(Level.WARNING, "Failed to get message from mbox: {0}", ex.getMessage()); //NON-NLS + } + return null; + } + + } } diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java index 704e3b2e14..32ab28352e 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.thunderbirdparser; +import com.google.common.collect.Iterables; import com.pff.PSTAttachment; import com.pff.PSTException; import com.pff.PSTFile; @@ -29,6 +30,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Scanner; import java.util.logging.Level; @@ -55,112 +57,193 @@ class PstParser { * First four bytes of a pst file. */ private static int PST_HEADER = 0x2142444E; - private IngestServices services; /** * A map of PSTMessages to their Local path within the file's internal * directory structure. */ - private List results; - private StringBuilder errors; - + private final StringBuilder errors; + + private final IngestServices services; + + private PSTFile pstFile; + private long fileID; + + private int failureCount = 0; + PstParser(IngestServices services) { - results = new ArrayList<>(); - this.services = services; errors = new StringBuilder(); + this.services = services; } enum ParseResult { OK, ERROR, ENCRYPT; } - /** - * Parse and extract email messages from the pst/ost file. - * - * @param file A pst or ost file. - * - * @return ParseResult: OK on success, ERROR on an error, ENCRYPT if failed - * because the file is encrypted. + * Create an instance of PSTFile for the given File object. + * + * The constructor for PSTFile object will throw a generic PSTException if the + * file is encrypted. + * PSTFile.java + * + * @param file File to open + * @param fileID File id for use when creating the EmailMessage objects + * @return ParserResult value OK if the PSTFile was successfully created, + * ENCRYPT will be returned for PSTExceptions that matches at specific + * message or IllegalArgumentExceptions */ - ParseResult parse(File file, long fileID) { - PSTFile pstFile; - long failures; + ParseResult open(File file, long fileID) { + if(file == null) { + return ParseResult.ERROR; + } + try { pstFile = new PSTFile(file); - failures = processFolder(pstFile.getRootFolder(), "\\", true, fileID); - if (failures > 0) { - addErrorMessage( - NbBundle.getMessage(this.getClass(), "PstParser.parse.errMsg.failedToParseNMsgs", failures)); + } catch(PSTException ex) { + // This is the message thrown from the PSTFile constructor if it + // detects that the file is encrypted. + if(ex.getMessage().equals("Only unencrypted and compressable PST files are supported at this time")) { //NON-NLS + logger.log(Level.INFO, "Found encrypted PST file."); //NON-NLS + return ParseResult.ENCRYPT; } - return ParseResult.OK; - } catch (PSTException | IOException ex) { String msg = file.getName() + ": Failed to create internal java-libpst PST file to parse:\n" + ex.getMessage(); //NON-NLS - logger.log(Level.WARNING, msg); + logger.log(Level.WARNING, msg, ex); return ParseResult.ERROR; - } catch (IllegalArgumentException ex) { + } catch (IOException ex) { + String msg = file.getName() + ": Failed to create internal java-libpst PST file to parse:\n" + ex.getMessage(); //NON-NLS + logger.log(Level.WARNING, msg, ex); + return ParseResult.ERROR; + } catch (IllegalArgumentException ex) { // Not sure if this is true, was in previous version of code. logger.log(Level.INFO, "Found encrypted PST file."); //NON-NLS return ParseResult.ENCRYPT; } + + return ParseResult.OK; } - + /** - * Get the results of the parsing. - * - * @return + * Creates an EmailMessage iterator for pstFile. These Email objects will be + * complete and with all available information. + * + * @return A instance of an EmailMessage Iterator */ - List getResults() { - return results; + Iterator getEmailMessageIterator() { + if(pstFile == null) { + return null; + } + + Iterable iterable = null; + + try { + iterable = getEmaiMessageIterator(pstFile.getRootFolder(), "\\", fileID, true); + } catch (PSTException | IOException ex) { + logger.log(Level.WARNING, String.format("Exception thrown while parsing fileID: %d", fileID), ex); + } + + if(iterable == null) { + return null; + } + + return iterable.iterator(); } - + + /** + * Get a List of EmailMessages which contain only the information needed for + * threading the emails. + * + * @return A list of EmailMessage or an empty list if non were found. + */ + List getPartialEmailMessages() { + List messages = new ArrayList<>(); + Iterator iterator = getPartialEmailMessageIterator(); + if(iterator != null) { + while(iterator.hasNext()) { + messages.add(iterator.next()); + } + } + + return messages; + } + + /** + * + * @return + */ String getErrors() { return errors.toString(); } - - /** - * Process this folder and all subfolders, adding every email found to - * results. Accumulates the folder hierarchy path as it navigates the folder - * structure. - * - * @param folder The folder to navigate and process - * @param path The path to the folder within the pst/ost file's directory - * structure - * - * @throws PSTException - * @throws IOException + + int getFailureCount() { + return failureCount; + } + + /** + * Get an Iterator to which will iterate over the PSTFile, but return EmailMessages + * with only the information needed for putting the emails into threads. + * + * @return A EmailMessage iterator or null if no messages where found */ - private long processFolder(PSTFolder folder, String path, boolean root, long fileID) { - String newPath = (root ? path : path + "\\" + folder.getDisplayName()); - long failCount = 0L; // Number of emails that failed - if (folder.hasSubfolders()) { - List subFolders; - try { - subFolders = folder.getSubFolders(); - } catch (PSTException | IOException ex) { - subFolders = new ArrayList<>(); - logger.log(Level.INFO, "java-libpst exception while getting subfolders: {0}", ex.getMessage()); //NON-NLS - } - - for (PSTFolder f : subFolders) { - failCount += processFolder(f, newPath, false, fileID); - } + private Iterator getPartialEmailMessageIterator() { + if(pstFile == null) { + return null; } - - if (folder.getContentCount() != 0) { - PSTMessage email; - // A folder's children are always emails, never other folders. - try { - while ((email = (PSTMessage) folder.getNextChild()) != null) { - results.add(extractEmailMessage(email, newPath, fileID)); - } - } catch (PSTException | IOException ex) { - failCount++; - logger.log(Level.INFO, "java-libpst exception while getting emails from a folder: {0}", ex.getMessage()); //NON-NLS - } + + Iterable iterable = null; + + try { + iterable = getEmaiMessageIterator(pstFile.getRootFolder(), "\\", fileID, false); + } catch (PSTException | IOException ex) { + logger.log(Level.WARNING, String.format("Exception thrown while parsing fileID: %d", fileID), ex); } - - return failCount; + + if(iterable == null) { + return null; + } + + return iterable.iterator(); } + /** + * Creates an Iterable object of Email messages for the given folder. + * + * @param folder PSTFolder to process + * @param path String path to folder + * @param fileID FileID of the AbstractFile folder was found in + * @param partialEmail Whether or not fill the EMailMessage with all data + * + * @return An Iterable for iterating email message, or null if there were no + * messages or children in folder. + * + * @throws PSTException + * @throws IOException + */ + private Iterable getEmaiMessageIterator(PSTFolder folder, String path, long fileID, boolean wholeMsg) throws PSTException, IOException { + Iterable iterable = null; + + if(folder.getContentCount() > 0) { + iterable = new PstEmailIterator(folder, path, fileID, wholeMsg).getIterable(); + } + + if(folder.hasSubfolders()) { + List subFolders = folder.getSubFolders(); + for(PSTFolder subFolder: subFolders) { + String newpath = path + "\\" + subFolder.getDisplayName(); + Iterable subIterable = getEmaiMessageIterator(subFolder, newpath, fileID, wholeMsg); + if(subIterable == null) { + continue; + } + + if(iterable != null) { + iterable = Iterables.concat(iterable, subIterable); + } else { + iterable = subIterable; + } + + } + } + + return iterable; + } /** * Create an EmailMessage from a PSTMessage. * @@ -214,6 +297,33 @@ class PstParser { return email; } + + /** + * Create an EmailMessage from a PSTMessage with only the information + * needed for threading emails. + * + * @return EmailMessage object with only some information, not all of the msg. + */ + private EmailMessage extractPartialEmailMessage(PSTMessage msg) { + EmailMessage email = new EmailMessage(); + email.setSubject(msg.getSubject()); + email.setId(msg.getDescriptorNodeId()); + email.setMessageID(msg.getInternetMessageId()); + String inReplyToID = msg.getInReplyToId(); + email.setInReplyToID(inReplyToID); + List references = extractReferences(msg.getTransportMessageHeaders()); + if (inReplyToID != null && !inReplyToID.isEmpty()) { + if (references == null) { + references = new ArrayList<>(); + references.add(inReplyToID); + } else if (!references.contains(inReplyToID)) { + references.add(inReplyToID); + } + } + email.setReferences(references); + + return email; + } /** * Add the attachments within the PSTMessage to the EmailMessage. @@ -388,5 +498,95 @@ class PstParser { } return null; + } + + /** + * A iterator for processing the PST email folder structure and returning + * instances of the EmailMessage object. + */ + private final class PstEmailIterator implements Iterator { + + private final PSTFolder folder; + private EmailMessage current; + private EmailMessage next; + + private final String currentPath; + private final long fileID; + private final boolean wholeMsg; + + /** + * Class constructor, initializes the "next" message; + * + * @param folder PSTFolder object to iterate across + * @param path String path value to the location of folder + * @param fileID Long fileID of the abstract file this PSTFolder was found + */ + PstEmailIterator(PSTFolder folder, String path, long fileID, boolean wholeMsg) { + this.folder = folder; + this.fileID = fileID; + this.currentPath = path; + this.wholeMsg = wholeMsg; + + if(folder.getContentCount() > 0) { + try { + PSTMessage message = (PSTMessage)folder.getNextChild(); + if(message != null) { + if(wholeMsg) { + next = extractEmailMessage(message, currentPath, fileID); + } else { + next = extractPartialEmailMessage(message); + } + } + } catch (PSTException | IOException ex) { + failureCount++; + logger.log(Level.WARNING, String.format("Unable to extract emails for path: %s file ID: %d ", path, fileID), ex); + } + } + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public EmailMessage next() { + + current = next; + + try { + PSTMessage message = (PSTMessage)folder.getNextChild(); + if(message != null) { + if(wholeMsg) { + next = extractEmailMessage(message, currentPath, fileID); + } else { + next = extractPartialEmailMessage(message); + } + } else { + next = null; + } + } catch (PSTException | IOException ex) { + logger.log(Level.WARNING, String.format("Unable to extract emails for path: %s file ID: %d ", currentPath, fileID), ex); + failureCount++; + next = null; + } + + return current; + } + + /** + * Get a wrapped Iterable version of PstEmailIterator + * + * @return Iterable wrapping this class + */ + Iterable getIterable(){ + return new Iterable(){ + @Override + public Iterator iterator() { + return PstEmailIterator.this; + } + }; + } + } } diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java index 33da281def..b413400404 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.logging.Level; @@ -183,51 +184,53 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { } PstParser parser = new PstParser(services); - PstParser.ParseResult result = parser.parse(file, abstractFile.getId()); + PstParser.ParseResult result = parser.open(file, abstractFile.getId()); - if (result == PstParser.ParseResult.OK) { - // parse success: Process email and add artifacts - processEmails(parser.getResults(), abstractFile); - - } else if (result == PstParser.ParseResult.ENCRYPT) { - // encrypted pst: Add encrypted file artifact - try { - BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED); - artifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.encryptionFileLevel"))); + switch( result) { + case OK: + processEmails(parser.getPartialEmailMessages(), parser.getEmailMessageIterator(), abstractFile); + break; + case ENCRYPT: + // encrypted pst: Add encrypted file artifact try { - // index the artifact for keyword search - blackboard.indexArtifact(artifact); - } catch (Blackboard.BlackboardException ex) { - MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName()); - logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS - } + BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED); + artifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.encryptionFileLevel"))); - services.fireModuleDataEvent(new ModuleDataEvent(EmailParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED)); - } catch (TskCoreException ex) { - logger.log(Level.INFO, "Failed to add encryption attribute to file: {0}", abstractFile.getName()); //NON-NLS - } - } else { - // parsing error: log message - postErrorMessage( - NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg", - abstractFile.getName()), - NbBundle.getMessage(this.getClass(), - "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details")); - logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS - return ProcessResult.ERROR; + try { + // index the artifact for keyword search + blackboard.indexArtifact(artifact); + } catch (Blackboard.BlackboardException ex) { + MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName()); + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS + } + + services.fireModuleDataEvent(new ModuleDataEvent(EmailParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED)); + } catch (TskCoreException ex) { + logger.log(Level.INFO, "Failed to add encryption attribute to file: {0}", abstractFile.getName()); //NON-NLS + } + break; + default: + // parsing error: log message + postErrorMessage( + NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg", + abstractFile.getName()), + NbBundle.getMessage(this.getClass(), + "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details")); + logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS + return ProcessResult.ERROR; } if (file.delete() == false) { logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS } - String errors = parser.getErrors(); - if (errors.isEmpty() == false) { - postErrorMessage( - NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg2", - abstractFile.getName()), errors); - } +// String errors = parser.getErrors(); +// if (errors.isEmpty() == false) { +// postErrorMessage( +// NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg2", +// abstractFile.getName()), errors); +// } return ProcessResult.OK; } @@ -281,21 +284,29 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { return ProcessResult.OK; } - MboxParser parser = new MboxParser(services, emailFolder); - List emails = parser.parse(file, abstractFile.getId()); - processEmails(emails, abstractFile); + MboxParser emailIterator = MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId()); + List emails = new ArrayList<>(); + if(emailIterator != null) { + while(emailIterator.hasNext()) { + EmailMessage emailMessage = emailIterator.next(); + if(emailMessage != null) { + emails.add(emailMessage); + } + } + + String errors = emailIterator.getErrors(); + if (!errors.isEmpty()) { + postErrorMessage( + NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg2", + abstractFile.getName()), errors); + } + } + processEmails(emails, MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId()), abstractFile); if (file.delete() == false) { logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS } - String errors = parser.getErrors(); - if (errors.isEmpty() == false) { - postErrorMessage( - NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg2", - abstractFile.getName()), errors); - } - return ProcessResult.OK; } @@ -434,7 +445,50 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { context.addFilesToJob(derivedFiles); services.fireModuleDataEvent(new ModuleDataEvent(EmailParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG)); } + + private void processEmails(List partialEmailsForThreading, Iterator fullMessageIterator, AbstractFile abstractFile) { + // Putting try/catch around this to catch any exception and still allow + // the creation of the artifacts to continue. + try{ + EmailMessageThreader.threadMessages(partialEmailsForThreading, String.format("%d", abstractFile.getId())); + } catch(Exception ex) { + logger.log(Level.WARNING, String.format("Exception thrown parsing emails from %s", abstractFile.getName()), ex); + } + + List derivedFiles = new ArrayList<>(); + int msgCnt = 0; + while(fullMessageIterator.hasNext()) { + EmailMessage current = fullMessageIterator.next(); + + if(current == null) { + continue; + } + + if(partialEmailsForThreading.size() > msgCnt) { + EmailMessage threaded = partialEmailsForThreading.get(msgCnt++); + + if(threaded.getMessageID().equals(current.getMessageID()) && + threaded.getSubject().equals(current.getSubject())) { + current.setMessageThreadID(threaded.getMessageThreadID()); + } + } + + BlackboardArtifact msgArtifact = addEmailArtifact(current, abstractFile); + + if ((msgArtifact != null) && (current.hasAttachment())) { + derivedFiles.addAll(handleAttachments(current.getAttachments(), abstractFile, msgArtifact )); + } + } + + if (derivedFiles.isEmpty() == false) { + for (AbstractFile derived : derivedFiles) { + services.fireModuleContentEvent(new ModuleContentEvent(derived)); + } + } + context.addFilesToJob(derivedFiles); + services.fireModuleDataEvent(new ModuleDataEvent(EmailParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG)); + } /** * Add the given attachments as derived files and reschedule them for * ingest. From 2c5e115d24083033c64b15edb4715a2b80711d9b Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 3 Jul 2019 10:23:36 -0400 Subject: [PATCH 09/69] Added call log tab cvt and removed the code from the message tab for call logs --- .../BlackboardArtifactDateComparator.java | 101 ++++++++++++ .../relationships/Bundle.properties-MERGED | 2 + .../relationships/CallLogNode.java | 55 +++++++ .../relationships/CallLogViewer.form | 26 +++ .../relationships/CallLogViewer.java | 148 ++++++++++++++++++ .../CallLogsChildNodeFactory.java | 88 +++++++++++ .../relationships/MessageNode.java | 125 +++++---------- .../relationships/MessageViewer.java | 6 +- .../MessagesChildNodeFactory.java | 17 +- .../relationships/RelationshipBrowser.form | 1 + .../relationships/RelationshipBrowser.java | 3 + .../RelationshipsNodeUtilities.java | 53 +++++++ .../relationships/ThreadChildNodeFactory.java | 46 +----- 13 files changed, 523 insertions(+), 148 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/communications/relationships/BlackboardArtifactDateComparator.java create mode 100755 Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java create mode 100755 Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.form create mode 100755 Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.java create mode 100755 Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java create mode 100755 Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/BlackboardArtifactDateComparator.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/BlackboardArtifactDateComparator.java new file mode 100755 index 0000000000..cef2ddd3d9 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/BlackboardArtifactDateComparator.java @@ -0,0 +1,101 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.communications.relationships; + +import java.util.Comparator; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.TskCoreException; + + /** +* A comparator class for comparing BlackboardArtifacts of type +* TSK_EMAIL_MSG, TSK_MESSAGE, and TSK_CALLLOG by their respective creation +* date-time. +*/ +class BlackboardArtifactDateComparator implements Comparator { + static final int ACCENDING = 1; + static final int DECENDING = -1; + + private static final Logger logger = Logger.getLogger(BlackboardArtifactDateComparator.class.getName()); + + private final int direction; + + BlackboardArtifactDateComparator(int direction) { + this.direction = direction; + } + + @Override + public int compare(BlackboardArtifact bba1, BlackboardArtifact bba2) { + + BlackboardAttribute attribute1 = null; + BlackboardAttribute attribute2 = null; + // Inializing to Long.MAX_VALUE so that if a BlackboardArtifact of + // any unexpected type is passed in, it will bubble to the top of + // the list. + long dateTime1 = Long.MAX_VALUE; + long dateTime2 = Long.MAX_VALUE; + + if (bba1 != null) { + BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba1.getArtifactTypeID()); + if (fromID != null) { + try { + switch (fromID) { + case TSK_EMAIL_MSG: + attribute1 = bba1.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT)); + break; + case TSK_MESSAGE: + attribute1 = bba1.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME)); + break; + case TSK_CALLLOG: + attribute1 = bba1.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START)); + break; + default: + attribute1 = null; + break; + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to compare attributes for artifact %d", bba1.getArtifactID()), ex); + } + } + } + + if (bba2 != null) { + BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba2.getArtifactTypeID()); + if (fromID != null) { + try { + switch (fromID) { + case TSK_EMAIL_MSG: + attribute2 = bba2.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT)); + break; + case TSK_MESSAGE: + attribute2 = bba2.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME)); + break; + case TSK_CALLLOG: + attribute2 = bba2.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START)); + break; + default: + attribute2 = null; + break; + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to compare attributes for artifact %d", bba2.getArtifactID()), ex); + } + } + } + + if (attribute1 != null) { + dateTime1 = attribute1.getValueLong(); + } + + if (attribute2 != null) { + dateTime2 = attribute2.getValueLong(); + } + + return Long.compare(dateTime1, dateTime2) * direction; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED index f82411bf9d..9cfcae847e 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED @@ -1,3 +1,5 @@ +CallLogViewer_noCallLogs= +CallLogViewer_title=Call Logs ContactDetailsPane.nameLabel.text=Placeholder ContactNode_Email=Email Address ContactNode_Home_Number=Home Number diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java new file mode 100755 index 0000000000..787f6ed9d4 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java @@ -0,0 +1,55 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.communications.relationships; + +import org.openide.nodes.Sheet; +import static org.sleuthkit.autopsy.communications.relationships.RelationshipsNodeUtilities.getAttributeDisplayString; +import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; +import org.sleuthkit.autopsy.datamodel.NodeProperty; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG; +import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; + +/** + * + * + */ +public class CallLogNode extends BlackboardArtifactNode { + + CallLogNode(BlackboardArtifact artifact) { + super(artifact); + } + + @Override + protected Sheet createSheet() { + Sheet sheet = super.createSheet(); + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); + if (sheetSet == null) { + sheetSet = Sheet.createPropertiesSet(); + sheet.put(sheetSet); + } + + final BlackboardArtifact artifact = getArtifact(); + + BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); + if (null != fromID && fromID != TSK_CALLLOG) { + return sheet; + } + + sheetSet.put(createNode(TSK_PHONE_NUMBER_FROM, artifact)); + sheetSet.put(createNode(TSK_PHONE_NUMBER_TO, artifact)); + sheetSet.put(createNode(TSK_DATETIME_START, artifact)); + + return sheet; + } + + NodeProperty createNode(BlackboardAttribute.ATTRIBUTE_TYPE type, BlackboardArtifact artifact) { + return new NodeProperty<>(type.getLabel(), type.getDisplayName(), "", getAttributeDisplayString(artifact, type)); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.form new file mode 100755 index 0000000000..18ea7b177a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.form @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.java new file mode 100755 index 0000000000..c421609fdc --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.java @@ -0,0 +1,148 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.communications.relationships; + +import javax.swing.JPanel; +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.AbstractNode; +import org.openide.nodes.Children; +import org.openide.nodes.NodeAdapter; +import org.openide.nodes.NodeMemberEvent; +import org.openide.util.Lookup; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.corecomponents.TableFilterNode; +import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START; + +/** + * + * + */ +final class CallLogViewer extends javax.swing.JPanel implements RelationshipsViewer { + + private final CallLogsChildNodeFactory nodeFactory; + + @Messages({ + "CallLogViewer_title=Call Logs", + "CallLogViewer_noCallLogs=" + }) + + /** + * Creates new form CallLogViewer + */ + public CallLogViewer() { + initComponents(); + + nodeFactory = new CallLogsChildNodeFactory(null); + outlineViewPanel.hideOutlineView(Bundle.CallLogViewer_noCallLogs()); + + outlineViewPanel.getOutlineView().setPropertyColumns( + TSK_PHONE_NUMBER_FROM.getLabel(), TSK_PHONE_NUMBER_FROM.getDisplayName(), + TSK_PHONE_NUMBER_TO.getLabel(), TSK_PHONE_NUMBER_TO.getDisplayName(), + TSK_DATETIME_START.getLabel(), TSK_DATETIME_START.getDisplayName() + ); + + Outline outline = outlineViewPanel.getOutlineView().getOutline(); + outline.setRootVisible(false); + + TableColumnModel columnModel = outline.getColumnModel(); + ETableColumn column = (ETableColumn) columnModel.getColumn(0); + ((ETableColumnModel) columnModel).setColumnHidden(column, true); + + outlineViewPanel.getExplorerManager().setRootContext( + new TableFilterNode( + new DataResultFilterNode( + new AbstractNode(Children.create(nodeFactory, true)), outlineViewPanel.getExplorerManager()), true)); + + outlineViewPanel.getExplorerManager().getRootContext().addNodeListener(new NodeAdapter(){ + @Override + public void childrenAdded(NodeMemberEvent nme) { + updateOutlineViewPanel(); + } + + @Override + public void childrenRemoved(NodeMemberEvent nme) { + updateOutlineViewPanel(); + } + }); + + } + + /** + * 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() { + java.awt.GridBagConstraints gridBagConstraints; + + outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel(); + + setLayout(new java.awt.GridBagLayout()); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(15, 15, 15, 15); + add(outlineViewPanel, gridBagConstraints); + }// //GEN-END:initComponents + + @Override + public String getDisplayName() { + return Bundle.CallLogViewer_title(); + } + + @Override + public JPanel getPanel() { + return this; + } + + @Override + public void setSelectionInfo(SelectionInfo info) { + nodeFactory.refresh(info); + } + + @Override + public Lookup getLookup() { + return outlineViewPanel.getLookup(); + } + + private void updateOutlineViewPanel() { + int nodeCount = outlineViewPanel.getExplorerManager().getRootContext().getChildren().getNodesCount(); + if(nodeCount == 0) { + outlineViewPanel.hideOutlineView(Bundle.ContactsViewer_noContacts_message()); + } else { + outlineViewPanel.showOutlineView(); + } + } + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java new file mode 100755 index 0000000000..567ed2d65c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java @@ -0,0 +1,88 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obt ain 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.communications.relationships; + +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Node; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * + * + */ +final class CallLogsChildNodeFactory extends ChildFactory{ + + private static final Logger logger = Logger.getLogger(CallLogsChildNodeFactory.class.getName()); + + private SelectionInfo selectionInfo; + + CallLogsChildNodeFactory(SelectionInfo selectionInfo) { + this.selectionInfo = selectionInfo; + } + + void refresh(SelectionInfo selectionInfo) { + this.selectionInfo = selectionInfo; + refresh(true); + } + + @Override + protected boolean createKeys(List list) { + + if(selectionInfo == null) { + return true; + } + + final Set relationshipSources; + try { + relationshipSources = selectionInfo.getRelationshipSources(); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Failed to load relationship sources.", ex); //NON-NLS + return false; + } + + + for(Content content: relationshipSources) { + if( !(content instanceof BlackboardArtifact)){ + continue; + } + + BlackboardArtifact bba = (BlackboardArtifact) content; + BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba.getArtifactTypeID()); + + if ( fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG) { + list.add(bba); + } + } + + list.sort(new BlackboardArtifactDateComparator(BlackboardArtifactDateComparator.ACCENDING)); + + return true; + } + + @Override + protected Node createNodeForKey(BlackboardArtifact key) { + return new CallLogNode(key); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java index d34bc084da..1c18ba5952 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.communications.relationships; -import java.util.TimeZone; import java.util.logging.Level; import javax.swing.Action; import org.apache.commons.lang3.StringUtils; @@ -27,20 +26,18 @@ import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_TO; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT; -import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME; -import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.autopsy.communications.Utils; +import static org.sleuthkit.autopsy.communications.relationships.RelationshipsNodeUtilities.getAttributeDisplayString; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE; /** * Wraps a BlackboardArtifact as an AbstractNode for use in an OutlookView @@ -48,7 +45,6 @@ import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; class MessageNode extends BlackboardArtifactNode { public static final String UNTHREADED_ID = ""; - public static final String CALL_LOG_ID = ""; private static final Logger logger = Logger.getLogger(MessageNode.class.getName()); @@ -87,93 +83,46 @@ class MessageNode extends BlackboardArtifactNode { } sheetSet.put(new NodeProperty<>("Type", Bundle.MessageNode_Node_Property_Type(), "", getDisplayName())); //NON-NLS - - final BlackboardArtifact artifact = getArtifact(); - if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID()) { - sheetSet.put(new NodeProperty<>("ThreadID", "ThreadID","",CALL_LOG_ID)); //NON-NLS - } else { - sheetSet.put(new NodeProperty<>("ThreadID", "ThreadID","",threadID == null ? UNTHREADED_ID : threadID)); //NON-NLS - } - BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); - if (null != fromID) { - //Consider refactoring this to reduce boilerplate - switch (fromID) { - case TSK_EMAIL_MSG: - sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "", - StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_FROM), " \t\n;"))); //NON-NLS - sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "", - StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_TO), " \t\n;"))); //NON-NLS - sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "", - getAttributeDisplayString(artifact, TSK_DATETIME_SENT))); //NON-NLS - sheetSet.put(new NodeProperty<>("Subject", Bundle.MessageNode_Node_Property_Subject(), "", - getAttributeDisplayString(artifact, TSK_SUBJECT))); //NON-NLS - try { - sheetSet.put(new NodeProperty<>("Attms", Bundle.MessageNode_Node_Property_Attms(), "", artifact.getChildrenCount())); //NON-NLS - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex); //NON-NLS - } - - break; - case TSK_MESSAGE: - sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "", - getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM))); //NON-NLS - sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "", - getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO))); //NON-NLS - sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "", - getAttributeDisplayString(artifact, TSK_DATETIME))); //NON-NLS - sheetSet.put(new NodeProperty<>("Subject", Bundle.MessageNode_Node_Property_Subject(), "", - getAttributeDisplayString(artifact, TSK_SUBJECT))); //NON-NLS - try { - sheetSet.put(new NodeProperty<>("Attms", Bundle.MessageNode_Node_Property_Attms(), "", artifact.getChildrenCount())); //NON-NLS - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex); //NON-NLS - } - break; - case TSK_CALLLOG: - sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "", - getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM))); //NON-NLS - sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "", - getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO))); //NON-NLS - sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "", - getAttributeDisplayString(artifact, TSK_DATETIME_START))); //NON-NLS - break; - default: - break; - } + + if(fromID == null || + (fromID != TSK_EMAIL_MSG && + fromID != TSK_MESSAGE)) { + return sheet; } - - return sheet; - } - - /** - * - * Get the display string for the attribute of the given type from the given - * artifact. - * - * @param artifact the value of artifact - * @param attributeType the value of TSK_SUBJECT1 - * - * @return The display string, or an empty string if there is no such - * attribute or an an error. - */ - private static String getAttributeDisplayString(final BlackboardArtifact artifact, final BlackboardAttribute.ATTRIBUTE_TYPE attributeType) { + + sheetSet.put(new NodeProperty<>("ThreadID", "ThreadID","",threadID == null ? UNTHREADED_ID : threadID)); //NON-NLS + sheetSet.put(new NodeProperty<>("Subject", Bundle.MessageNode_Node_Property_Subject(), "", + getAttributeDisplayString(artifact, TSK_SUBJECT))); //NON-NLS try { - BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attributeType.getTypeID()))); - if (attribute == null) { - return ""; - } else if (attributeType.getValueType() == DATETIME) { - return TimeUtilities.epochToTime(attribute.getValueLong(), - TimeZone.getTimeZone(Utils.getUserPreferredZoneId())); - } else { - return attribute.getDisplayString(); - } - } catch (TskCoreException tskCoreException) { - logger.log(Level.WARNING, "Error getting attribute value.", tskCoreException); //NON-NLS - return ""; + sheetSet.put(new NodeProperty<>("Attms", Bundle.MessageNode_Node_Property_Attms(), "", artifact.getChildrenCount())); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex); //NON-NLS } + + switch (fromID) { + case TSK_EMAIL_MSG: + sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "", + StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_FROM), " \t\n;"))); //NON-NLS + sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "", + StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_TO), " \t\n;"))); //NON-NLS + sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "", + getAttributeDisplayString(artifact, TSK_DATETIME_SENT))); //NON-NLS + break; + case TSK_MESSAGE: + sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "", + getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM))); //NON-NLS + sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "", + getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO))); //NON-NLS + sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "", + getAttributeDisplayString(artifact, TSK_DATETIME))); //NON-NLS + break; + default: + break; + } + return sheet; } /** diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageViewer.java index 4c7be60e80..617992dab5 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageViewer.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageViewer.java @@ -229,11 +229,7 @@ public class MessageViewer extends JPanel implements RelationshipsViewer { if (!subject.isEmpty()) { threadNameLabel.setText(subject); } else { - if (threadIDList.contains(MessageNode.CALL_LOG_ID)) { - threadNameLabel.setText(Bundle.MessageViewer_viewMessage_calllogs()); - } else { - threadNameLabel.setText(Bundle.MessageViewer_viewMessage_unthreaded()); - } + threadNameLabel.setText(Bundle.MessageViewer_viewMessage_unthreaded()); } showMessagesPane(); diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesChildNodeFactory.java index 5039ce225e..a68b95e9e4 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesChildNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesChildNodeFactory.java @@ -23,12 +23,9 @@ import java.util.Set; import java.util.logging.Level; import org.openide.nodes.ChildFactory; import org.openide.nodes.Node; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; @@ -92,8 +89,7 @@ public class MessagesChildNodeFactory extends ChildFactory{ BlackboardArtifact bba = (BlackboardArtifact) content; BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba.getArtifactTypeID()); - if (fromID != BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG - && fromID != BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG + if (fromID != BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG && fromID != BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) { continue; } @@ -102,12 +98,8 @@ public class MessagesChildNodeFactory extends ChildFactory{ // To achive this assign any artifact that does not have a threadID // the "UNTHREADED_ID" // All call logs will default to a single call logs thread - String artifactThreadID; - if (fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG) { - artifactThreadID = MessageNode.CALL_LOG_ID; - } else { - artifactThreadID = MessageNode.UNTHREADED_ID; - } + String artifactThreadID = MessageNode.UNTHREADED_ID; + BlackboardAttribute attribute = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID)); if(attribute != null) { @@ -116,8 +108,7 @@ public class MessagesChildNodeFactory extends ChildFactory{ if(threadIDs == null || threadIDs.contains(artifactThreadID)) { list.add(bba); - } - + } } } catch (TskCoreException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.form index ef5a5d1357..3289340e2c 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.form +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.form @@ -11,6 +11,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.java index 1431bb9fa1..9f6309d6af 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.java @@ -35,6 +35,7 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider private final ContactsViewer contactsViewer; private final SummaryViewer summaryViewer; private final MediaViewer mediaViewer; + private final CallLogViewer callLogViewer; private final ModifiableProxyLookup proxyLookup; @@ -47,11 +48,13 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider contactsViewer = new ContactsViewer(); summaryViewer = new SummaryViewer(); mediaViewer = new MediaViewer(); + callLogViewer = new CallLogViewer(); proxyLookup = new ModifiableProxyLookup(messagesViewer.getLookup()); tabPane.add(summaryViewer.getDisplayName(), summaryViewer); tabPane.add(messagesViewer.getDisplayName(), messagesViewer); + tabPane.add(callLogViewer.getDisplayName(), callLogViewer); tabPane.add(contactsViewer.getDisplayName(), contactsViewer); tabPane.add(mediaViewer.getDisplayName(), mediaViewer); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java new file mode 100755 index 0000000000..2d2aaef0de --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java @@ -0,0 +1,53 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.communications.relationships; + +import java.util.Comparator; +import java.util.TimeZone; +import java.util.logging.Level; +import org.sleuthkit.autopsy.communications.Utils; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME; +import org.sleuthkit.datamodel.TimeUtilities; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * + * + */ +public class RelationshipsNodeUtilities { + + private static final Logger logger = Logger.getLogger(RelationshipsNodeUtilities.class.getName()); + /** + * + * Get the display string for the attribute of the given type from the given + * artifact. + * + * @param artifact the value of artifact + * @param attributeType the value of TSK_SUBJECT1 + * + * @return The display string, or an empty string if there is no such + * attribute or an an error. + */ + static String getAttributeDisplayString(final BlackboardArtifact artifact, final BlackboardAttribute.ATTRIBUTE_TYPE attributeType) { + try { + BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attributeType.getTypeID()))); + if (attribute == null) { + return ""; + } else if (attributeType.getValueType() == DATETIME) { + return TimeUtilities.epochToTime(attribute.getValueLong(), + TimeZone.getTimeZone(Utils.getUserPreferredZoneId())); + } else { + return attribute.getDisplayString(); + } + } catch (TskCoreException tskCoreException) { + logger.log(Level.WARNING, "Error getting attribute value.", tskCoreException); //NON-NLS + return ""; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java index 1cf987d132..8f3afd5519 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java @@ -126,13 +126,8 @@ final class ThreadChildNodeFactory extends ChildFactory { // We want email and message artifacts that do not have "threadIDs" to appear as one thread in the UI // To achive this assign any artifact that does not have a threadID // the "UNTHREADED_ID" - // All call logs will default to a single call logs thread - String threadID; - if (fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG) { - threadID = MessageNode.CALL_LOG_ID; - } else { - threadID = MessageNode.UNTHREADED_ID; - } + String threadID = MessageNode.UNTHREADED_ID; + BlackboardAttribute attribute = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID)); if(attribute != null) { @@ -176,44 +171,11 @@ final class ThreadChildNodeFactory extends ChildFactory { if (attribute != null) { return new ThreadNode(bba, attribute.getValueString(), preferredAction); } else { - if (bba.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID()) { - return new CallLogNode(); - } else { - // Only one of these should occur. - return new UnthreadedNode(); - } + // Only one of these should occur. + return new UnthreadedNode(); } } - /** - * This node represents the "call log" thread. - */ - final class CallLogNode extends AbstractNode { - /** - * Construct an instance of a CallLogNode. - */ - CallLogNode() { - super(Children.LEAF); - setDisplayName("Call Logs"); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/unthreaded.png" ); - } - - @Override - protected Sheet createSheet() { - Sheet sheet = super.createSheet(); - Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); - if (sheetSet == null) { - sheetSet = Sheet.createPropertiesSet(); - sheet.put(sheetSet); - } - - // Give this node a threadID of "CALL_LOG_ID" - sheetSet.put(new NodeProperty<>("ThreadID", "ThreadID","",MessageNode.CALL_LOG_ID)); - - return sheet; - } - } - /** * This node represents the "unthreaded" thread. */ From 0013c31977304174fbba9f2e68f3ee898a6056be Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 3 Jul 2019 10:36:51 -0400 Subject: [PATCH 10/69] cleaned up imports in ThreadChildNodeFactory --- .../communications/relationships/ThreadChildNodeFactory.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java index 8f3afd5519..6d66c0b032 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java @@ -29,13 +29,10 @@ import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; From 8fef328308b5747578c54f1fbca126cd88da9bf3 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 3 Jul 2019 11:14:02 -0400 Subject: [PATCH 11/69] Update the email threader, android wwf and SMS code to assign unique cross ingest run threadIDs --- InternalPythonModules/android/textmessage.py | 4 +++- InternalPythonModules/android/wwfmessage.py | 5 ++++- .../thunderbirdparser/EmailMessageThreader.java | 11 +++++------ .../ThunderbirdMboxFileIngestModule.java | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/InternalPythonModules/android/textmessage.py b/InternalPythonModules/android/textmessage.py index ef8fa498c8..803cfa5b96 100644 --- a/InternalPythonModules/android/textmessage.py +++ b/InternalPythonModules/android/textmessage.py @@ -29,6 +29,7 @@ from java.sql import SQLException from java.sql import Statement from java.util.logging import Level from java.util import ArrayList +from java.util import UUID from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.casemodule.services import Blackboard from org.sleuthkit.autopsy.casemodule.services import FileManager @@ -95,6 +96,7 @@ class TextMessageAnalyzer(general.AndroidComponentAnalyzer): ds = Case.getCurrentCase().getSleuthkitCase().getDataSource(datasourceObjId) deviceID = ds.getDeviceId() deviceAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.DEVICE, deviceID, general.MODULE_NAME, abstractFile) + uuid = UUID.randomUUID().toString() resultSet = None try: @@ -106,7 +108,7 @@ class TextMessageAnalyzer(general.AndroidComponentAnalyzer): read = resultSet.getInt("read") # may be unread = 0, read = 1 subject = resultSet.getString("subject") # message subject body = resultSet.getString("body") # message body - thread_id = "{0}_{1}".format(abstractFile.getId(), resultSet.getInt("thread_id")) + thread_id = "{0}-{1}".format(uuid, resultSet.getInt("thread_id")) attributes = ArrayList() artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); #create Message artifact and then add attributes from result set. if resultSet.getString("type") == "1": diff --git a/InternalPythonModules/android/wwfmessage.py b/InternalPythonModules/android/wwfmessage.py index faf62bb4e2..bf00ea54c0 100644 --- a/InternalPythonModules/android/wwfmessage.py +++ b/InternalPythonModules/android/wwfmessage.py @@ -27,6 +27,7 @@ from java.sql import SQLException from java.sql import Statement from java.util.logging import Level from java.util import ArrayList +from java.util import UUID from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.casemodule.services import Blackboard from org.sleuthkit.autopsy.casemodule.services import FileManager @@ -99,6 +100,7 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer): ds = Case.getCurrentCase().getSleuthkitCase().getDataSource(datasourceObjId) deviceID = ds.getDeviceId() deviceAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.DEVICE, deviceID, general.MODULE_NAME, abstractFile) + uuid = UUID.randomUUID().toString() resultSet = None try: @@ -110,6 +112,7 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer): created_at = resultSet.getLong("datetime") user_id = resultSet.getString("user_id") # the ID of the user who sent the message. game_id = resultSet.getString("game_id") # ID of the game which the the message was sent. + thread_id = "{0}-{1}".format(uuid, user_id) attributes = ArrayList() artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) # create a call log and then add attributes from result set. @@ -118,7 +121,7 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer): attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MSG_ID, general.MODULE_NAME, game_id)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, message)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "Words With Friends Message")) - attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID, general.MODULE_NAME, user_id)) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID, general.MODULE_NAME, thread_id)) artifact.addAttributes(attributes) diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/EmailMessageThreader.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/EmailMessageThreader.java index 6e003a54a6..431c7cc74c 100755 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/EmailMessageThreader.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/EmailMessageThreader.java @@ -25,6 +25,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; /** * Given a list of email messages arranges the message into threads using the @@ -41,7 +42,7 @@ final class EmailMessageThreader { private EmailMessageThreader(){} - public static void threadMessages(List emailMessages, String threadIDPrefix) { + public static void threadMessages(List emailMessages) { EmailMessageThreader instance = new EmailMessageThreader(); Map id_table = instance.createIDTable(emailMessages); @@ -51,7 +52,7 @@ final class EmailMessageThreader { Set finalRootSet = instance.groupBySubject(rootSet); - instance.assignThreadIDs(finalRootSet, threadIDPrefix); + instance.assignThreadIDs(finalRootSet); } /** @@ -413,12 +414,10 @@ final class EmailMessageThreader { * * @param IDPrefix A string to make the threadIDs unique. */ - private void assignThreadIDs(Set containerSet, String IDPrefix) { - int threadCounter = 0; - + private void assignThreadIDs(Set containerSet) { for(EmailContainer container: containerSet) { // Generate a threadID - String threadID = String.format("%s-%d", IDPrefix, threadCounter++); + String threadID = UUID.randomUUID().toString(); // Add the IDs to this thread addThreadID(container, threadID); } diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java index 33da281def..40580dc346 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java @@ -413,7 +413,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { // Putting try/catch around this to catch any exception and still allow // the creation of the artifacts to continue. try{ - EmailMessageThreader.threadMessages(emails, String.format("%d", abstractFile.getId())); + EmailMessageThreader.threadMessages(emails); } catch(Exception ex) { logger.log(Level.WARNING, String.format("Exception thrown parsing emails from %s", abstractFile.getName()), ex); } From 44b08f6a3a7796f1557f7db20e0aaafcc677323f Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 3 Jul 2019 14:39:24 -0400 Subject: [PATCH 12/69] Made adjustments based on comments from Brian --- .../relationships/Bundle.properties-MERGED | 3 + .../relationships/CallLogNode.java | 71 ++++++++++++++++--- .../relationships/CallLogViewer.form | 3 +- .../relationships/CallLogViewer.java | 26 ++++--- .../CallLogsChildNodeFactory.java | 3 +- .../RelationshipsNodeUtilities.java | 1 - 6 files changed, 79 insertions(+), 28 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED index 9cfcae847e..58eafd7bff 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED @@ -1,4 +1,7 @@ +CallLogViewer_device_label=Device +CallLogViewer_duration_label=Duration(seconds) CallLogViewer_noCallLogs= +CallLogViewer_recipient_label=Recipient CallLogViewer_title=Call Logs ContactDetailsPane.nameLabel.text=Placeholder ContactNode_Email=Email Address diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java index 787f6ed9d4..c3531ef591 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java @@ -6,24 +6,38 @@ package org.sleuthkit.autopsy.communications.relationships; import org.openide.nodes.Sheet; +import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.communications.Utils; import static org.sleuthkit.autopsy.communications.relationships.RelationshipsNodeUtilities.getAttributeDisplayString; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; import org.sleuthkit.autopsy.datamodel.NodeProperty; +import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.BlackboardArtifact; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG; import org.sleuthkit.datamodel.BlackboardAttribute; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER; +import org.sleuthkit.datamodel.TskCoreException; /** - * - * + * A BlackboardArtifactNode for Calllogs. */ -public class CallLogNode extends BlackboardArtifactNode { +final class CallLogNode extends BlackboardArtifactNode { - CallLogNode(BlackboardArtifact artifact) { - super(artifact); + final static String DURATION_PROP = "duration"; + + CallLogNode(BlackboardArtifact artifact) { + super(artifact, Utils.getIconFilePath(Account.Type.DEVICE)); + + try { + setDisplayName(artifact.getDataSource().getName()); + } catch (TskCoreException ex) { + Exceptions.printStackTrace(ex); + } } @Override @@ -41,15 +55,52 @@ public class CallLogNode extends BlackboardArtifactNode { if (null != fromID && fromID != TSK_CALLLOG) { return sheet; } - - sheetSet.put(createNode(TSK_PHONE_NUMBER_FROM, artifact)); - sheetSet.put(createNode(TSK_PHONE_NUMBER_TO, artifact)); - sheetSet.put(createNode(TSK_DATETIME_START, artifact)); + String phoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM); + if(phoneNumber == null || phoneNumber.isEmpty()) { + phoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO); + } + + long duration = -1; + try{ + duration = getCallDuration(artifact); + } catch(TskCoreException ex) { + + } + + sheetSet.put(createNode(TSK_DATETIME_START, artifact)); + sheetSet.put(createNode(TSK_DIRECTION, artifact)); + sheetSet.put(new NodeProperty<>(TSK_PHONE_NUMBER.getLabel(), TSK_PHONE_NUMBER.getDisplayName(), "", phoneNumber)); + if(duration != -1) { + sheetSet.put(new NodeProperty<>("duration", "Duration", "", Long.toString(duration))); + } + return sheet; } NodeProperty createNode(BlackboardAttribute.ATTRIBUTE_TYPE type, BlackboardArtifact artifact) { - return new NodeProperty<>(type.getLabel(), type.getDisplayName(), "", getAttributeDisplayString(artifact, type)); + return new NodeProperty<>(type.getLabel(), type.getDisplayName(), type.getDisplayName(), getAttributeDisplayString(artifact, type)); + } + + long getCallDuration(BlackboardArtifact artifact) throws TskCoreException { + BlackboardAttribute startAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(TSK_DATETIME_START.getTypeID()))); + BlackboardAttribute endAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(TSK_DATETIME_END.getTypeID()))); + + if(startAttribute == null || endAttribute == null) { + return -1; + } + + return endAttribute.getValueLong() - startAttribute.getValueLong(); + } + + /** + * Circumvent DataResultFilterNode's slightly odd delegation to + * BlackboardArtifactNode.getSourceName(). + * + * @return the displayName of this Node, which is the type. + */ + @Override + public String getSourceName() { + return getDisplayName(); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.form index 18ea7b177a..cab67d7f86 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.form +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.form @@ -1,6 +1,6 @@ -
    + @@ -11,6 +11,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.java index c421609fdc..344335aeea 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.java @@ -19,9 +19,6 @@ package org.sleuthkit.autopsy.communications.relationships; import javax.swing.JPanel; -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.AbstractNode; @@ -32,13 +29,13 @@ import org.openide.util.Lookup; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION; /** * - * + * CallLogViewer Panel */ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsViewer { @@ -46,7 +43,10 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie @Messages({ "CallLogViewer_title=Call Logs", - "CallLogViewer_noCallLogs=" + "CallLogViewer_noCallLogs=", + "CallLogViewer_recipient_label=Recipient", + "CallLogViewer_duration_label=Duration(seconds)", + "CallLogViewer_device_label=Device" }) /** @@ -59,17 +59,15 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie outlineViewPanel.hideOutlineView(Bundle.CallLogViewer_noCallLogs()); outlineViewPanel.getOutlineView().setPropertyColumns( - TSK_PHONE_NUMBER_FROM.getLabel(), TSK_PHONE_NUMBER_FROM.getDisplayName(), - TSK_PHONE_NUMBER_TO.getLabel(), TSK_PHONE_NUMBER_TO.getDisplayName(), - TSK_DATETIME_START.getLabel(), TSK_DATETIME_START.getDisplayName() + TSK_DIRECTION.getLabel(), TSK_DIRECTION.getDisplayName(), + TSK_PHONE_NUMBER.getLabel(), Bundle.CallLogViewer_recipient_label(), + TSK_DATETIME_START.getLabel(), TSK_DATETIME_START.getDisplayName(), + CallLogNode.DURATION_PROP, Bundle.CallLogViewer_duration_label() ); Outline outline = outlineViewPanel.getOutlineView().getOutline(); outline.setRootVisible(false); - - TableColumnModel columnModel = outline.getColumnModel(); - ETableColumn column = (ETableColumn) columnModel.getColumn(0); - ((ETableColumnModel) columnModel).setColumnHidden(column, true); + ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.CallLogViewer_device_label()); outlineViewPanel.getExplorerManager().setRootContext( new TableFilterNode( diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java index 567ed2d65c..326522dd47 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java @@ -29,8 +29,7 @@ import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; /** - * - * + *A ChildFactory for CallLog artifacts. */ final class CallLogsChildNodeFactory extends ChildFactory{ diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java index 2d2aaef0de..fd2509aba9 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java @@ -5,7 +5,6 @@ */ package org.sleuthkit.autopsy.communications.relationships; -import java.util.Comparator; import java.util.TimeZone; import java.util.logging.Level; import org.sleuthkit.autopsy.communications.Utils; From df6152dbdf34f7b908467cef5d14d3d506dcebf5 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Fri, 5 Jul 2019 11:42:38 -0400 Subject: [PATCH 13/69] modified filterPane layout and fixed chart tool tips --- .../autopsy/communications/Bundle.properties | 21 +- .../communications/Bundle.properties-MERGED | 23 +- .../communications/CVTTopComponent.form | 93 +++--- .../communications/CVTTopComponent.java | 29 +- .../autopsy/communications/FiltersPanel.form | 150 ++++----- .../autopsy/communications/FiltersPanel.java | 107 +++---- .../communications/VisualizationPanel.form | 23 +- .../communications/VisualizationPanel.java | 299 +++++++++--------- 8 files changed, 377 insertions(+), 368 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 4b08fd2d45..81e765e145 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -22,15 +22,13 @@ CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualiz CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize VisualizationPanel.fitGraphButton.text= VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin. -VisualizationPanel.zoomLabel.text=100% -VisualizationPanel.jLabel2.text=Zoom: -VisualizationPanel.fitZoomButton.toolTipText=fit visualization +VisualizationPanel.fitZoomButton.toolTipText=Fit Visualization VisualizationPanel.fitZoomButton.text= -VisualizationPanel.zoomActualButton.toolTipText=reset zoom +VisualizationPanel.zoomActualButton.toolTipText=Reset Zoom VisualizationPanel.zoomActualButton.text= -VisualizationPanel.zoomInButton.toolTipText=Zoom in +VisualizationPanel.zoomInButton.toolTipText=Zoom In VisualizationPanel.zoomInButton.text= -VisualizationPanel.zoomOutButton.toolTipText=Zoom out +VisualizationPanel.zoomOutButton.toolTipText=Zoom Out VisualizationPanel.zoomOutButton.text= VisualizationPanel.fastOrganicLayoutButton.text= VisualizationPanel.backButton.text_1= @@ -40,11 +38,14 @@ VisualizationPanel.hierarchyLayoutButton.text=Hierarchical VisualizationPanel.clearVizButton.text_1= VisualizationPanel.snapshotButton.text_1=Snapshot Report VisualizationPanel.clearVizButton.actionCommand= -VisualizationPanel.backButton.toolTipText=Click to go back -VisualizationPanel.forwardButton.toolTipText=Click to go forward -VisualizationPanel.fastOrganicLayoutButton.toolTipText=Click to redraw the chart -VisualizationPanel.clearVizButton.toolTipText=Click to clear the chart +VisualizationPanel.backButton.toolTipText=Click to Go Back +VisualizationPanel.forwardButton.toolTipText=Click to Go Forward +VisualizationPanel.fastOrganicLayoutButton.toolTipText=Click to Redraw Chart +VisualizationPanel.clearVizButton.toolTipText=Click to Clear Chart FiltersPanel.limitHeaderLabel.text=Communications Limit: FiltersPanel.mostRecentLabel.text=Most Recent: FiltersPanel.limitErrorMsgLabel.text=Invalid integer value. VisualizationPanel.forwardButton.text= +VisualizationPanel.zoomPercentLabel.text=100% +VisualizationPanel.zoomLabel.text=Zoom: +VisualizationPanel.snapshotButton.toolTipText=Generate Snapshot Report diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED index b314af1cb9..0f07f9a084 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED @@ -57,6 +57,8 @@ CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualiz CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize VisualizationPanel.fitGraphButton.text= VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin. +VisualizationPanel.fitZoomButton.toolTipText=Fit Visualization +VisualizationPanel.fitZoomButton.text= # {0} - layout name VisualizationPanel.layoutFail.text={0} layout failed. Try a different layout. # {0} - layout name @@ -65,15 +67,11 @@ VisualizationPanel.lockAction.pluralText=Lock Selected Accounts VisualizationPanel.lockAction.singularText=Lock Selected Account VisualizationPanel.unlockAction.pluralText=Unlock Selected Accounts VisualizationPanel.unlockAction.singularText=Unlock Selected Account -VisualizationPanel.zoomLabel.text=100% -VisualizationPanel.jLabel2.text=Zoom: -VisualizationPanel.fitZoomButton.toolTipText=fit visualization -VisualizationPanel.fitZoomButton.text= -VisualizationPanel.zoomActualButton.toolTipText=reset zoom +VisualizationPanel.zoomActualButton.toolTipText=Reset Zoom VisualizationPanel.zoomActualButton.text= -VisualizationPanel.zoomInButton.toolTipText=Zoom in +VisualizationPanel.zoomInButton.toolTipText=Zoom In VisualizationPanel.zoomInButton.text= -VisualizationPanel.zoomOutButton.toolTipText=Zoom out +VisualizationPanel.zoomOutButton.toolTipText=Zoom Out VisualizationPanel.zoomOutButton.text= VisualizationPanel.fastOrganicLayoutButton.text= VisualizationPanel.backButton.text_1= @@ -83,14 +81,17 @@ VisualizationPanel.hierarchyLayoutButton.text=Hierarchical VisualizationPanel.clearVizButton.text_1= VisualizationPanel.snapshotButton.text_1=Snapshot Report VisualizationPanel.clearVizButton.actionCommand= -VisualizationPanel.backButton.toolTipText=Click to go back -VisualizationPanel.forwardButton.toolTipText=Click to go forward -VisualizationPanel.fastOrganicLayoutButton.toolTipText=Click to redraw the chart -VisualizationPanel.clearVizButton.toolTipText=Click to clear the chart +VisualizationPanel.backButton.toolTipText=Click to Go Back +VisualizationPanel.forwardButton.toolTipText=Click to Go Forward +VisualizationPanel.fastOrganicLayoutButton.toolTipText=Click to Redraw Chart +VisualizationPanel.clearVizButton.toolTipText=Click to Clear Chart FiltersPanel.limitHeaderLabel.text=Communications Limit: FiltersPanel.mostRecentLabel.text=Most Recent: FiltersPanel.limitErrorMsgLabel.text=Invalid integer value. VisualizationPanel.forwardButton.text= +VisualizationPanel.zoomPercentLabel.text=100% +VisualizationPanel.zoomLabel.text=Zoom: +VisualizationPanel.snapshotButton.toolTipText=Generate Snapshot Report VisualizationPanel_action_dialogs_title=Communications VisualizationPanel_action_name_text=Snapshot Report VisualizationPanel_module_name=Communications diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form index bc16bb6da2..a7bc5379c3 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form @@ -16,61 +16,72 @@ - - - - - - - - - - - + - + - + - + - - - - - - - - - + + - + + + + + + + + + + + - - - - - - - - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 0032793584..1000131501 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.communications; import com.google.common.eventbus.Subscribe; import java.awt.Component; -import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; @@ -28,6 +27,7 @@ import java.awt.Insets; import java.util.List; import java.util.stream.Collectors; import javax.swing.ImageIcon; +import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import org.openide.util.Lookup; import org.openide.util.NbBundle; @@ -80,6 +80,9 @@ public final class CVTTopComponent extends TopComponent { CVTEvents.getCVTEventBus().register(vizPanel); CVTEvents.getCVTEventBus().register(accountsBrowser); CVTEvents.getCVTEventBus().register(filtersPane); + + mainSplitPane.setResizeWeight(0.5); + mainSplitPane.setDividerLocation(0.25); } @Subscribe @@ -96,37 +99,28 @@ public final class CVTTopComponent extends TopComponent { private void initComponents() { GridBagConstraints gridBagConstraints; + mainSplitPane = new JSplitPane(); + filtersPane = new FiltersPanel(); browseVisualizeTabPane = new JTabbedPane(); accountsBrowser = new AccountsBrowser(); vizPanel = new VisualizationPanel(); - filtersPane = new FiltersPanel(); setLayout(new GridBagLayout()); + mainSplitPane.setLeftComponent(filtersPane); + browseVisualizeTabPane.setFont(new Font("Tahoma", 0, 18)); // NOI18N browseVisualizeTabPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1"), new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N browseVisualizeTabPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizPanel.TabConstraints.tabTitle_1"), new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizPanel); // NOI18N - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 0; - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.weightx = 0.75; - gridBagConstraints.weighty = 1.0; - gridBagConstraints.insets = new Insets(15, 0, 15, 15); - add(browseVisualizeTabPane, gridBagConstraints); + mainSplitPane.setRightComponent(browseVisualizeTabPane); browseVisualizeTabPane.getAccessibleContext().setAccessibleName(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName")); // NOI18N gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.weightx = 0.25; + gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; - gridBagConstraints.insets = new Insets(15, 15, 15, 5); - add(filtersPane, gridBagConstraints); + add(mainSplitPane, gridBagConstraints); }// //GEN-END:initComponents @@ -134,6 +128,7 @@ public final class CVTTopComponent extends TopComponent { private AccountsBrowser accountsBrowser; private JTabbedPane browseVisualizeTabPane; private FiltersPanel filtersPane; + private JSplitPane mainSplitPane; private VisualizationPanel vizPanel; // End of variables declaration//GEN-END:variables diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form index 1aba67e76c..33fe6d5228 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form @@ -16,77 +16,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -95,7 +24,7 @@ - + @@ -108,7 +37,7 @@ - + @@ -199,7 +128,7 @@ - + @@ -293,7 +222,7 @@ - + @@ -490,6 +419,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 76c2f189ad..09a915b7dd 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -474,54 +474,6 @@ final public class FiltersPanel extends JPanel { setLayout(new java.awt.GridBagLayout()); - topPane.setLayout(new java.awt.GridBagLayout()); - - filtersTitleLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/funnel.png"))); // NOI18N - filtersTitleLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.filtersTitleLabel.text")); // NOI18N - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; - gridBagConstraints.weightx = 1.0; - topPane.add(filtersTitleLabel, gridBagConstraints); - - refreshButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); // NOI18N - refreshButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.refreshButton.text")); // NOI18N - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 0; - gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; - topPane.add(refreshButton, gridBagConstraints); - - applyFiltersButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/tick.png"))); // NOI18N - applyFiltersButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.applyFiltersButton.text")); // NOI18N - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 0; - gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; - gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 5); - topPane.add(applyFiltersButton, gridBagConstraints); - - needsRefreshLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.needsRefreshLabel.text")); // NOI18N - needsRefreshLabel.setForeground(new java.awt.Color(255, 0, 0)); - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; - gridBagConstraints.gridwidth = 3; - gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; - gridBagConstraints.weightx = 1.0; - topPane.add(needsRefreshLabel, gridBagConstraints); - - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_END; - gridBagConstraints.weightx = 1.0; - add(topPane, gridBagConstraints); - scrollPane.setBorder(null); mainPanel.setLayout(new java.awt.GridBagLayout()); @@ -581,7 +533,7 @@ final public class FiltersPanel extends JPanel { gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 3; + gridBagConstraints.gridy = 4; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; @@ -649,7 +601,7 @@ final public class FiltersPanel extends JPanel { gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; + gridBagConstraints.gridy = 3; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; @@ -726,7 +678,7 @@ final public class FiltersPanel extends JPanel { gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; + gridBagConstraints.gridy = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.ipady = 100; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; @@ -808,16 +760,65 @@ final public class FiltersPanel extends JPanel { gridBagConstraints.insets = new java.awt.Insets(15, 0, 0, 0); mainPanel.add(accountTypesPane, gridBagConstraints); + topPane.setLayout(new java.awt.GridBagLayout()); + + filtersTitleLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/funnel.png"))); // NOI18N + filtersTitleLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.filtersTitleLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + topPane.add(filtersTitleLabel, gridBagConstraints); + + refreshButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); // NOI18N + refreshButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.refreshButton.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + topPane.add(refreshButton, gridBagConstraints); + + applyFiltersButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/tick.png"))); // NOI18N + applyFiltersButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.applyFiltersButton.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 5); + topPane.add(applyFiltersButton, gridBagConstraints); + + needsRefreshLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.needsRefreshLabel.text")); // NOI18N + needsRefreshLabel.setForeground(new java.awt.Color(255, 0, 0)); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + topPane.add(needsRefreshLabel, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_END; + gridBagConstraints.weightx = 1.0; + mainPanel.add(topPane, gridBagConstraints); + scrollPane.setViewportView(mainPanel); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; + gridBagConstraints.gridy = 0; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; - gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0); + gridBagConstraints.insets = new java.awt.Insets(9, 15, 0, 0); add(scrollPane, gridBagConstraints); }// //GEN-END:initComponents diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index f2e3052f47..e85901a1ba 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -105,10 +105,10 @@ - - + + @@ -134,8 +134,8 @@ - + @@ -241,13 +241,6 @@
    - - - - - - - @@ -255,6 +248,13 @@ + + + + + + + @@ -320,6 +320,9 @@ + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 10732c630f..8a5404bb1a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -213,7 +213,7 @@ final public class VisualizationPanel extends JPanel { lockedVertexModel.registerhandler(this); final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt) - -> zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale())); + -> zoomPercentLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale())); graph.getView().addListener(mxEvent.SCALE, scaleListener); graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener); @@ -373,223 +373,220 @@ final public class VisualizationPanel extends JPanel { // //GEN-BEGIN:initComponents private void initComponents() { - splitPane = new javax.swing.JSplitPane(); - borderLayoutPanel = new javax.swing.JPanel(); - placeHolderPanel = new javax.swing.JPanel(); - jTextArea1 = new javax.swing.JTextArea(); - toolbar = new javax.swing.JPanel(); - fastOrganicLayoutButton = new javax.swing.JButton(); - zoomOutButton = new javax.swing.JButton(); - zoomInButton = new javax.swing.JButton(); - zoomActualButton = new javax.swing.JButton(); - fitZoomButton = new javax.swing.JButton(); - jLabel2 = new javax.swing.JLabel(); - zoomLabel = new javax.swing.JLabel(); - clearVizButton = new javax.swing.JButton(); - jSeparator2 = new javax.swing.JToolBar.Separator(); - backButton = new javax.swing.JButton(); - forwardButton = new javax.swing.JButton(); - snapshotButton = new javax.swing.JButton(); - jSeparator3 = new javax.swing.JToolBar.Separator(); - jSeparator4 = new javax.swing.JToolBar.Separator(); - notificationsJFXPanel = new javafx.embed.swing.JFXPanel(); + splitPane = new JSplitPane(); + borderLayoutPanel = new JPanel(); + placeHolderPanel = new JPanel(); + jTextArea1 = new JTextArea(); + toolbar = new JPanel(); + fastOrganicLayoutButton = new JButton(); + zoomOutButton = new JButton(); + zoomInButton = new JButton(); + zoomActualButton = new JButton(); + fitZoomButton = new JButton(); + zoomLabel = new JLabel(); + zoomPercentLabel = new JLabel(); + clearVizButton = new JButton(); + jSeparator2 = new JToolBar.Separator(); + backButton = new JButton(); + forwardButton = new JButton(); + snapshotButton = new JButton(); + jSeparator3 = new JToolBar.Separator(); + jSeparator4 = new JToolBar.Separator(); + notificationsJFXPanel = new JFXPanel(); - setLayout(new java.awt.BorderLayout()); + setLayout(new BorderLayout()); splitPane.setDividerLocation(800); splitPane.setResizeWeight(0.5); - borderLayoutPanel.setLayout(new java.awt.BorderLayout()); + borderLayoutPanel.setLayout(new BorderLayout()); - jTextArea1.setBackground(new java.awt.Color(240, 240, 240)); + jTextArea1.setBackground(new Color(240, 240, 240)); jTextArea1.setColumns(20); jTextArea1.setLineWrap(true); jTextArea1.setRows(5); - jTextArea1.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jTextArea1.text")); // NOI18N + jTextArea1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jTextArea1.text")); // NOI18N - org.jdesktop.layout.GroupLayout placeHolderPanelLayout = new org.jdesktop.layout.GroupLayout(placeHolderPanel); + GroupLayout placeHolderPanelLayout = new GroupLayout(placeHolderPanel); placeHolderPanel.setLayout(placeHolderPanelLayout); - placeHolderPanelLayout.setHorizontalGroup( - placeHolderPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(placeHolderPanelLayout.createSequentialGroup() .addContainerGap(250, Short.MAX_VALUE) - .add(jTextArea1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 424, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 424, GroupLayout.PREFERRED_SIZE) .addContainerGap(423, Short.MAX_VALUE)) ); - placeHolderPanelLayout.setVerticalGroup( - placeHolderPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(placeHolderPanelLayout.createSequentialGroup() - .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(jTextArea1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 47, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 47, GroupLayout.PREFERRED_SIZE) + .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); - borderLayoutPanel.add(placeHolderPanel, java.awt.BorderLayout.CENTER); + borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); - fastOrganicLayoutButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); // NOI18N - fastOrganicLayoutButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.text")); // NOI18N - fastOrganicLayoutButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.toolTipText")); // NOI18N + fastOrganicLayoutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); // NOI18N + fastOrganicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.text")); // NOI18N + fastOrganicLayoutButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.toolTipText")); // NOI18N fastOrganicLayoutButton.setFocusable(false); - fastOrganicLayoutButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM); - zoomOutButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N - zoomOutButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N - zoomOutButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.toolTipText")); // NOI18N + zoomOutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N + zoomOutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N + zoomOutButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.toolTipText")); // NOI18N zoomOutButton.setFocusable(false); - zoomOutButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - zoomOutButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - zoomOutButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER); + zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM); + zoomOutButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { zoomOutButtonActionPerformed(evt); } }); - zoomInButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); // NOI18N - zoomInButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.text")); // NOI18N - zoomInButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.toolTipText")); // NOI18N + zoomInButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); // NOI18N + zoomInButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.text")); // NOI18N + zoomInButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.toolTipText")); // NOI18N zoomInButton.setFocusable(false); - zoomInButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - zoomInButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - zoomInButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER); + zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM); + zoomInButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { zoomInButtonActionPerformed(evt); } }); - zoomActualButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); // NOI18N - zoomActualButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.text")); // NOI18N - zoomActualButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.toolTipText")); // NOI18N + zoomActualButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); // NOI18N + zoomActualButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.text")); // NOI18N + zoomActualButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.toolTipText")); // NOI18N zoomActualButton.setFocusable(false); - zoomActualButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - zoomActualButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - zoomActualButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER); + zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM); + zoomActualButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { zoomActualButtonActionPerformed(evt); } }); - fitZoomButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N - fitZoomButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.text")); // NOI18N - fitZoomButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.toolTipText")); // NOI18N + fitZoomButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N + fitZoomButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.text")); // NOI18N + fitZoomButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.toolTipText")); // NOI18N fitZoomButton.setFocusable(false); - fitZoomButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - fitZoomButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - fitZoomButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER); + fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM); + fitZoomButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { fitZoomButtonActionPerformed(evt); } }); - jLabel2.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jLabel2.text")); // NOI18N + zoomLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomLabel.text")); // NOI18N - zoomLabel.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomLabel.text")); // NOI18N + zoomPercentLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomPercentLabel.text")); // NOI18N - clearVizButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/broom.png"))); // NOI18N - clearVizButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.text_1")); // NOI18N - clearVizButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.toolTipText")); // NOI18N - clearVizButton.setActionCommand(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.actionCommand")); // NOI18N - clearVizButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + clearVizButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/broom.png"))); // NOI18N + clearVizButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.text_1")); // NOI18N + clearVizButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.toolTipText")); // NOI18N + clearVizButton.setActionCommand(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.actionCommand")); // NOI18N + clearVizButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { clearVizButtonActionPerformed(evt); } }); - jSeparator2.setOrientation(javax.swing.SwingConstants.VERTICAL); + jSeparator2.setOrientation(SwingConstants.VERTICAL); - backButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/resultset_previous.png"))); // NOI18N - backButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.backButton.text_1")); // NOI18N - backButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.backButton.toolTipText")); // NOI18N - backButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + backButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/resultset_previous.png"))); // NOI18N + backButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.backButton.text_1")); // NOI18N + backButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.backButton.toolTipText")); // NOI18N + backButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { backButtonActionPerformed(evt); } }); - forwardButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/resultset_next.png"))); // NOI18N - forwardButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.forwardButton.text")); // NOI18N - forwardButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.forwardButton.toolTipText")); // NOI18N - forwardButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING); - forwardButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + forwardButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/resultset_next.png"))); // NOI18N + forwardButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.forwardButton.text")); // NOI18N + forwardButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.forwardButton.toolTipText")); // NOI18N + forwardButton.setHorizontalTextPosition(SwingConstants.LEADING); + forwardButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { forwardButtonActionPerformed(evt); } }); - snapshotButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/report/images/image.png"))); // NOI18N - snapshotButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.snapshotButton.text_1")); // NOI18N - snapshotButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + snapshotButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/report/images/image.png"))); // NOI18N + snapshotButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.snapshotButton.text_1")); // NOI18N + snapshotButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.snapshotButton.toolTipText")); // NOI18N + snapshotButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { snapshotButtonActionPerformed(evt); } }); - jSeparator3.setOrientation(javax.swing.SwingConstants.VERTICAL); + jSeparator3.setOrientation(SwingConstants.VERTICAL); - jSeparator4.setOrientation(javax.swing.SwingConstants.VERTICAL); + jSeparator4.setOrientation(SwingConstants.VERTICAL); - org.jdesktop.layout.GroupLayout toolbarLayout = new org.jdesktop.layout.GroupLayout(toolbar); + GroupLayout toolbarLayout = new GroupLayout(toolbar); toolbar.setLayout(toolbarLayout); - toolbarLayout.setHorizontalGroup( - toolbarLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + toolbarLayout.setHorizontalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING) .add(toolbarLayout.createSequentialGroup() .addContainerGap() .add(backButton) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .addPreferredGap(LayoutStyle.RELATED) .add(forwardButton) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(jSeparator4, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .addPreferredGap(LayoutStyle.RELATED) + .add(jSeparator4, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) .add(fastOrganicLayoutButton) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .addPreferredGap(LayoutStyle.RELATED) .add(clearVizButton) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(jSeparator2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(jLabel2) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .addPreferredGap(LayoutStyle.RELATED) + .add(jSeparator2, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) .add(zoomLabel) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(zoomOutButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 32, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(zoomInButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 32, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(zoomActualButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 33, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(fitZoomButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 32, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(jSeparator3, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .addPreferredGap(LayoutStyle.RELATED) + .add(zoomPercentLabel) + .addPreferredGap(LayoutStyle.RELATED) + .add(zoomOutButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(zoomInButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(zoomActualButton, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(fitZoomButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(jSeparator3, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) .add(snapshotButton) - .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); - toolbarLayout.setVerticalGroup( - toolbarLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + toolbarLayout.setVerticalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING) .add(toolbarLayout.createSequentialGroup() .add(3, 3, 3) - .add(toolbarLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.CENTER) + .add(toolbarLayout.createParallelGroup(GroupLayout.CENTER) .add(fastOrganicLayoutButton) .add(zoomOutButton) - .add(zoomInButton, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(zoomActualButton, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(fitZoomButton, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(jLabel2) + .add(zoomInButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(zoomActualButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(fitZoomButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(zoomLabel) + .add(zoomPercentLabel) .add(clearVizButton) - .add(jSeparator2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(backButton) .add(forwardButton) .add(snapshotButton) - .add(jSeparator3, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(jSeparator4, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .add(jSeparator3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(jSeparator4, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .add(3, 3, 3)) ); - borderLayoutPanel.add(toolbar, java.awt.BorderLayout.PAGE_START); - borderLayoutPanel.add(notificationsJFXPanel, java.awt.BorderLayout.PAGE_END); + borderLayoutPanel.add(toolbar, BorderLayout.PAGE_START); + borderLayoutPanel.add(notificationsJFXPanel, BorderLayout.PAGE_END); splitPane.setLeftComponent(borderLayoutPanel); - add(splitPane, java.awt.BorderLayout.CENTER); + add(splitPane, BorderLayout.CENTER); }// //GEN-END:initComponents private void fitZoomButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fitZoomButtonActionPerformed @@ -880,26 +877,26 @@ final public class VisualizationPanel extends JPanel { } // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton backButton; - private javax.swing.JPanel borderLayoutPanel; - private javax.swing.JButton clearVizButton; - private javax.swing.JButton fastOrganicLayoutButton; - private javax.swing.JButton fitZoomButton; - private javax.swing.JButton forwardButton; - private javax.swing.JLabel jLabel2; - private javax.swing.JToolBar.Separator jSeparator2; - private javax.swing.JToolBar.Separator jSeparator3; - private javax.swing.JToolBar.Separator jSeparator4; - private javax.swing.JTextArea jTextArea1; - private javafx.embed.swing.JFXPanel notificationsJFXPanel; - private javax.swing.JPanel placeHolderPanel; - private javax.swing.JButton snapshotButton; - private javax.swing.JSplitPane splitPane; - private javax.swing.JPanel toolbar; - private javax.swing.JButton zoomActualButton; - private javax.swing.JButton zoomInButton; - private javax.swing.JLabel zoomLabel; - private javax.swing.JButton zoomOutButton; + private JButton backButton; + private JPanel borderLayoutPanel; + private JButton clearVizButton; + private JButton fastOrganicLayoutButton; + private JButton fitZoomButton; + private JButton forwardButton; + private JToolBar.Separator jSeparator2; + private JToolBar.Separator jSeparator3; + private JToolBar.Separator jSeparator4; + private JTextArea jTextArea1; + private JFXPanel notificationsJFXPanel; + private JPanel placeHolderPanel; + private JButton snapshotButton; + private JSplitPane splitPane; + private JPanel toolbar; + private JButton zoomActualButton; + private JButton zoomInButton; + private JLabel zoomLabel; + private JButton zoomOutButton; + private JLabel zoomPercentLabel; // End of variables declaration//GEN-END:variables /** From d8884b82c2155aafaefe443c8cbd4ff0052fd383 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Fri, 5 Jul 2019 15:37:11 -0400 Subject: [PATCH 14/69] Added image to contact viewer tab --- .../relationships/ContactDetailsPane.form | 5 +- .../relationships/ContactDetailsPane.java | 51 +++++++++++++++---- .../relationships/ContactNode.java | 22 ++++++++ .../relationships/ContactsViewer.java | 3 -- .../thunderbirdparser/VcardParser.java | 8 +-- 5 files changed, 67 insertions(+), 22 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.form index 671fac333e..0536cb5b11 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.form +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.form @@ -1,10 +1,6 @@
    - - - - @@ -15,6 +11,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java index ae456e0abb..3839dadfeb 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java @@ -18,24 +18,36 @@ */ package org.sleuthkit.autopsy.communications.relationships; +import java.util.logging.Level; +import javax.swing.ImageIcon; import org.openide.explorer.ExplorerManager; import org.openide.nodes.Node; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; /** * Displays the propertied of a ContactNode in a PropertySheet. */ public final class ContactDetailsPane extends javax.swing.JPanel implements ExplorerManager.Provider { - final private ExplorerManager explorerManager = new ExplorerManager(); + private static final Logger logger = Logger.getLogger(ContactDetailsPane.class.getName()); + + private final static String DEFAULT_IMAGE_PATH = "/org/sleuthkit/autopsy/images/face.png"; + + private final ExplorerManager explorerManager = new ExplorerManager(); + private final ImageIcon defaultImage; /** * Displays the propertied of a ContactNode in a PropertySheet. */ public ContactDetailsPane() { initComponents(); - this.setEnabled(false); - nameLabel.setText(""); + + defaultImage = new ImageIcon(ContactDetailsPane.class.getResource(DEFAULT_IMAGE_PATH)); } /** @@ -46,9 +58,16 @@ public final class ContactDetailsPane extends javax.swing.JPanel implements Expl public void setNode(Node[] nodes) { if (nodes != null && nodes.length == 1) { nameLabel.setText(nodes[0].getDisplayName()); + nameLabel.setIcon(null); propertySheet.setNodes(nodes); + + BlackboardArtifact n = nodes[0].getLookup().lookup(BlackboardArtifact.class); + if(n != null) { + nameLabel.setIcon(getImageForFromArtifact(n)); + } } else { nameLabel.setText(""); + nameLabel.setIcon(null); propertySheet.setNodes(null); } } @@ -57,12 +76,24 @@ public final class ContactDetailsPane extends javax.swing.JPanel implements Expl public ExplorerManager getExplorerManager() { return explorerManager; } - - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - nameLabel.setEnabled(enabled); - propertySheet.setEnabled(enabled); + + public ImageIcon getImageForFromArtifact(BlackboardArtifact artifact){ + ImageIcon image = defaultImage; + + try { + for(Content content: artifact.getChildren()) { + if(content instanceof AbstractFile) { + AbstractFile file = (AbstractFile)content; + file.getLocalAbsPath(); + + image = new ImageIcon(file.getLocalAbsPath()); + } + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to load image for contact: %d", artifact.getId()), ex); + } + + return image; } /** @@ -75,7 +106,6 @@ public final class ContactDetailsPane extends javax.swing.JPanel implements Expl private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; - messageContentViewer1 = new org.sleuthkit.autopsy.contentviewers.MessageContentViewer(); nameLabel = new javax.swing.JLabel(); propertySheet = new org.openide.explorer.propertysheet.PropertySheet(); @@ -107,7 +137,6 @@ public final class ContactDetailsPane extends javax.swing.JPanel implements Expl // Variables declaration - do not modify//GEN-BEGIN:variables - private org.sleuthkit.autopsy.contentviewers.MessageContentViewer messageContentViewer1; private javax.swing.JLabel nameLabel; private org.openide.explorer.propertysheet.PropertySheet propertySheet; // End of variables declaration//GEN-END:variables diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java index 9f275ec228..33b9d16724 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.communications.relationships; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.TimeZone; import java.util.logging.Level; @@ -36,6 +37,8 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBU import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.communications.Utils; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; /** * Extends BlackboardArtifactNode to override createSheet to create a contact @@ -112,6 +115,25 @@ final class ContactNode extends BlackboardArtifactNode { for (BlackboardAttribute bba : otherMap.values()) { sheetSet.put(new NodeProperty<>(bba.getAttributeType().getTypeName(), bba.getAttributeType().getDisplayName(), "", bba.getDisplayString())); } + + // Don't need these values to appear in the Contact property sheet. + sheetSet.remove("S"); + sheetSet.remove("C"); + + List children = artifact.getChildren(); + if(children != null) { + int count = 0; + String imageLabelPrefix = "Image"; + for(Content child: children) { + if(child instanceof AbstractFile) { + String imageLabel = imageLabelPrefix; + if(count > 0) { + imageLabel = imageLabelPrefix + "-" + count; + } + sheetSet.put(new NodeProperty<>(imageLabel, imageLabel, imageLabel, child.getName())); + } + } + } } catch (TskCoreException ex) { logger.log(Level.WARNING, "Error getting attribute values.", ex); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactsViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactsViewer.java index 4fdb400c87..33afae7251 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactsViewer.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactsViewer.java @@ -142,9 +142,6 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{ @Override public void setSelectionInfo(SelectionInfo info) { - contactPane.setNode(new Node[]{new AbstractNode(Children.LEAF)}); - contactPane.setEnabled(false); - nodeFactory.refresh(info); } diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java index 7e5a321313..a0fa8260aa 100755 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java @@ -164,8 +164,6 @@ final class VcardParser { private BlackboardArtifact addContactArtifact(VCard vcard, AbstractFile abstractFile) throws NoCurrentCaseException { List attributes = new ArrayList<>(); List accountInstances = new ArrayList<>(); - - extractPhotos(vcard, abstractFile); String name = ""; if (vcard.getFormattedName() != null) { @@ -229,6 +227,8 @@ final class VcardParser { List blackboardArtifacts = new ArrayList<>(); blackboardArtifacts.add(artifact); + extractPhotos(vcard, abstractFile, artifact); + // Add account relationships. if (deviceAccountInstance != null) { try { @@ -269,7 +269,7 @@ final class VcardParser { * * @throws NoCurrentCaseException if there is no open case. */ - private void extractPhotos(VCard vcard, AbstractFile abstractFile) throws NoCurrentCaseException { + private void extractPhotos(VCard vcard, AbstractFile abstractFile, BlackboardArtifact artifact) throws NoCurrentCaseException { String parentFileName = getUniqueName(abstractFile); // Skip files that already have been extracted. try { @@ -306,7 +306,7 @@ final class VcardParser { writeExtractedImage(extractedFilePath, data); derivedFilesCreated.add(fileManager.addDerivedFile(extractedFileName, getFileRelativePath(parentFileName, extractedFileName), data.length, abstractFile.getCtime(), abstractFile.getCrtime(), abstractFile.getAtime(), abstractFile.getAtime(), - true, abstractFile, null, EmailParserModuleFactory.getModuleName(), null, null, TskData.EncodingType.NONE)); + true, artifact, null, EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", TskData.EncodingType.NONE)); } catch (IOException | TskCoreException ex) { logger.log(Level.WARNING, String.format("Could not write image to '%s' (id=%d).", extractedFilePath, abstractFile.getId()), ex); //NON-NLS } From 632d3275d10ed394edbd2a36832c89f6d5913fa4 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Fri, 5 Jul 2019 16:15:51 -0400 Subject: [PATCH 15/69] Fixed bug in the cvt threads tab --- .../communications/relationships/ThreadChildNodeFactory.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java index 6d66c0b032..bf8eb5c509 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java @@ -117,7 +117,6 @@ final class ThreadChildNodeFactory extends ChildFactory { BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba.getArtifactTypeID()); if (fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG - || fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG || fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) { // We want email and message artifacts that do not have "threadIDs" to appear as one thread in the UI From d83a5b9c958411dea02162ecf79994c1cbacdf44 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 8 Jul 2019 13:02:38 -0400 Subject: [PATCH 16/69] replaced the device name with the device id --- .../relationships/CallLogNode.java | 11 ++--- .../CallLogsChildNodeFactory.java | 49 ++++++++++++++++++- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java index c3531ef591..9d13c55ce4 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java @@ -30,14 +30,9 @@ final class CallLogNode extends BlackboardArtifactNode { final static String DURATION_PROP = "duration"; - CallLogNode(BlackboardArtifact artifact) { - super(artifact, Utils.getIconFilePath(Account.Type.DEVICE)); - - try { - setDisplayName(artifact.getDataSource().getName()); - } catch (TskCoreException ex) { - Exceptions.printStackTrace(ex); - } + CallLogNode(BlackboardArtifact artifact, String deviceID) { + super(artifact, Utils.getIconFilePath(Account.Type.PHONE)); + setDisplayName(deviceID); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java index 326522dd47..a22f8f14cf 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java @@ -18,13 +18,21 @@ */ package org.sleuthkit.autopsy.communications.relationships; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.logging.Level; import org.openide.nodes.ChildFactory; import org.openide.nodes.Node; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.CommunicationsFilter; +import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; @@ -37,6 +45,8 @@ final class CallLogsChildNodeFactory extends ChildFactory{ private SelectionInfo selectionInfo; + private Map deviceIDMap = new HashMap(); + CallLogsChildNodeFactory(SelectionInfo selectionInfo) { this.selectionInfo = selectionInfo; } @@ -70,7 +80,7 @@ final class CallLogsChildNodeFactory extends ChildFactory{ BlackboardArtifact bba = (BlackboardArtifact) content; BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba.getArtifactTypeID()); - if ( fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG) { + if ( fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG) { list.add(bba); } } @@ -82,6 +92,41 @@ final class CallLogsChildNodeFactory extends ChildFactory{ @Override protected Node createNodeForKey(BlackboardArtifact key) { - return new CallLogNode(key); + String deviceID = ""; + try { + deviceID = getDeviceIDForDataSource(key.getDataSource().getName()); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to get account for artifact data source: artifactID = %d", key.getId()), ex); + } + + return new CallLogNode(key, deviceID); + } + + private String getDeviceIDForDataSource(String dataSourceName) throws NoCurrentCaseException, TskCoreException{ + + String deviceID = deviceIDMap.get(dataSourceName); + + if(deviceID == null) { + CommunicationsManager manager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager(); + CommunicationsFilter filter = new CommunicationsFilter(); + + List list = new ArrayList<>(); + list.add(dataSourceName); + + filter.addAndFilter(new CommunicationsFilter.DeviceFilter(list)); + + // This list should just have 1 item in it + List adiList = manager.getAccountDeviceInstancesWithRelationships(filter); + + if(adiList != null && adiList.size() > 0) { + deviceID = adiList.get(0).getDeviceId(); + } else { + deviceID = ""; + } + + deviceIDMap.put(dataSourceName, deviceID); + } + + return deviceID; } } From d5fa5ed363132153c7df15fe1c4c4b601b4287d8 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 8 Jul 2019 13:20:50 -0400 Subject: [PATCH 17/69] Added default contact image --- .../communications/images/defaultContact.png | Bin 0 -> 2560 bytes .../relationships/ContactDetailsPane.java | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100755 Core/src/org/sleuthkit/autopsy/communications/images/defaultContact.png diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/defaultContact.png b/Core/src/org/sleuthkit/autopsy/communications/images/defaultContact.png new file mode 100755 index 0000000000000000000000000000000000000000..80d697fac50c4808cdf7356db8d1c67f73b79682 GIT binary patch literal 2560 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 B89e|1 literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java index 3839dadfeb..5dba78cd5e 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java @@ -35,7 +35,7 @@ public final class ContactDetailsPane extends javax.swing.JPanel implements Expl private static final Logger logger = Logger.getLogger(ContactDetailsPane.class.getName()); - private final static String DEFAULT_IMAGE_PATH = "/org/sleuthkit/autopsy/images/face.png"; + private final static String DEFAULT_IMAGE_PATH = "/org/sleuthkit/autopsy/communications/images/defaultContact.png"; private final ExplorerManager explorerManager = new ExplorerManager(); private final ImageIcon defaultImage; From 800b1345fdd5bedee0193f1edca4dd5fd862dad9 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 8 Jul 2019 13:51:59 -0400 Subject: [PATCH 18/69] fixed bug with device id --- .../autopsy/communications/relationships/CallLogNode.java | 1 - .../relationships/CallLogsChildNodeFactory.java | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java index 9d13c55ce4..508421c58a 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java @@ -6,7 +6,6 @@ package org.sleuthkit.autopsy.communications.relationships; import org.openide.nodes.Sheet; -import org.openide.util.Exceptions; import org.sleuthkit.autopsy.communications.Utils; import static org.sleuthkit.autopsy.communications.relationships.RelationshipsNodeUtilities.getAttributeDisplayString; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java index a22f8f14cf..37c483fe2a 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java @@ -29,6 +29,7 @@ import org.openide.nodes.Node; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.CommunicationsFilter; @@ -112,8 +113,13 @@ final class CallLogsChildNodeFactory extends ChildFactory{ List list = new ArrayList<>(); list.add(dataSourceName); + + List typeList = new ArrayList(); + typeList.add(Account.Type.DEVICE); filter.addAndFilter(new CommunicationsFilter.DeviceFilter(list)); + filter.addAndFilter(new CommunicationsFilter.AccountTypeFilter(typeList)); + // This list should just have 1 item in it List adiList = manager.getAccountDeviceInstancesWithRelationships(filter); From 0fe4af33b510c7babf8fa246ea7a45a2fb908813 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 8 Jul 2019 13:57:23 -0400 Subject: [PATCH 19/69] Update FileViewer.java Add validate to replace paint to show registry tree. --- Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java index ead4199941..dddf0f6296 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java @@ -143,7 +143,7 @@ public class FileViewer extends javax.swing.JPanel implements DataContentViewer viewer.setFile(file); this.removeAll(); this.add(viewer.getComponent()); - this.repaint(); + this.validate(); } } From 6a2837af8107d101f0cb32c34b7483ecb9e44716 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 8 Jul 2019 14:44:24 -0400 Subject: [PATCH 20/69] created a calllogkey and moved the retrival of the deviceID to createKeys --- .../CallLogsChildNodeFactory.java | 64 +++++++++++++++---- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java index 37c483fe2a..427b6ff1e5 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.communications.relationships; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -28,6 +29,7 @@ import org.openide.nodes.ChildFactory; import org.openide.nodes.Node; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.communications.relationships.CallLogsChildNodeFactory.CallLogNodeKey; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.AccountDeviceInstance; @@ -40,7 +42,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** *A ChildFactory for CallLog artifacts. */ -final class CallLogsChildNodeFactory extends ChildFactory{ +final class CallLogsChildNodeFactory extends ChildFactory{ private static final Logger logger = Logger.getLogger(CallLogsChildNodeFactory.class.getName()); @@ -58,7 +60,7 @@ final class CallLogsChildNodeFactory extends ChildFactory{ } @Override - protected boolean createKeys(List list) { + protected boolean createKeys(List list) { if(selectionInfo == null) { return true; @@ -81,26 +83,28 @@ final class CallLogsChildNodeFactory extends ChildFactory{ BlackboardArtifact bba = (BlackboardArtifact) content; BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba.getArtifactTypeID()); - if ( fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG) { - list.add(bba); + if ( fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG) { + + String deviceID = ""; + try { + deviceID = getDeviceIDForDataSource(bba.getDataSource().getName()); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to get account for artifact data source: artifactID = %d", bba.getId()), ex); + } + + list.add(new CallLogNodeKey(bba, deviceID)); } } - list.sort(new BlackboardArtifactDateComparator(BlackboardArtifactDateComparator.ACCENDING)); + list.sort(new CallLogComparator(BlackboardArtifactDateComparator.ACCENDING)); return true; } - + @Override - protected Node createNodeForKey(BlackboardArtifact key) { - String deviceID = ""; - try { - deviceID = getDeviceIDForDataSource(key.getDataSource().getName()); - } catch (NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.WARNING, String.format("Unable to get account for artifact data source: artifactID = %d", key.getId()), ex); - } + protected Node createNodeForKey(CallLogNodeKey key) { - return new CallLogNode(key, deviceID); + return new CallLogNode(key.getArtifact(), key.getDeviceID()); } private String getDeviceIDForDataSource(String dataSourceName) throws NoCurrentCaseException, TskCoreException{ @@ -135,4 +139,36 @@ final class CallLogsChildNodeFactory extends ChildFactory{ return deviceID; } + + final class CallLogNodeKey{ + private final BlackboardArtifact artifact; + private final String deviceID; + + private CallLogNodeKey(BlackboardArtifact artifact, String deviceID) { + this.artifact = artifact; + this.deviceID = deviceID; + } + + BlackboardArtifact getArtifact() { + return artifact; + } + + String getDeviceID() { + return deviceID; + } + } + + final class CallLogComparator implements Comparator{ + + final BlackboardArtifactDateComparator comparator; + + CallLogComparator(int direction) { + comparator = new BlackboardArtifactDateComparator(direction); + } + + @Override + public int compare(CallLogNodeKey key1, CallLogNodeKey key2) { + return comparator.compare(key1.getArtifact(), key2.getArtifact()); + } + } } From aa57fe1050b50b7e335f0a00b43afaccdb35e25e Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 8 Jul 2019 15:36:12 -0400 Subject: [PATCH 21/69] cvt - removed code to disable device checkbox --- .../AccountDeviceInstanceNodeFactory.java | 4 ++-- .../communications/CVTTopComponent.java | 1 - .../autopsy/communications/FiltersPanel.java | 24 ++----------------- 3 files changed, 4 insertions(+), 25 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java index c0043d9ba1..c2a279b4ad 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java @@ -55,10 +55,10 @@ final class AccountDeviceInstanceNodeFactory extends ChildFactory map, boolean selected) { From 3b1d79744a0a57ceb5a7f4551e2b1f4f5df3ee1f Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 8 Jul 2019 15:37:55 -0400 Subject: [PATCH 22/69] cvt - cleaned up code in AccoutDeviceInstanceNodeFactory --- .../communications/AccountDeviceInstanceNodeFactory.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java index c2a279b4ad..35e1c2669f 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java @@ -55,10 +55,9 @@ final class AccountDeviceInstanceNodeFactory extends ChildFactory Date: Mon, 8 Jul 2019 15:38:30 -0400 Subject: [PATCH 23/69] cvt - cleaned up code in AccoutDeviceInstanceNodeFactory --- .../autopsy/communications/AccountDeviceInstanceNodeFactory.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java index 35e1c2669f..171cc32cb2 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java @@ -54,7 +54,6 @@ final class AccountDeviceInstanceNodeFactory extends ChildFactory accountDeviceInstancesWithRelationships = commsManager.getAccountDeviceInstancesWithRelationships(commsFilter); for (AccountDeviceInstance accountDeviceInstance : accountDeviceInstancesWithRelationships) { - //Filter out device accounts, in the table. long communicationsCount = commsManager.getRelationshipSourcesCount(accountDeviceInstance, commsFilter); accountDeviceInstanceKeys.add(new AccountDeviceInstanceKey(accountDeviceInstance, commsFilter, communicationsCount)); From 8748d597d055bd59ba437fa99f841b4dde3931d9 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 8 Jul 2019 17:26:26 -0400 Subject: [PATCH 24/69] 5280 allow sorting of logical imager results in dsp panel --- .../logicalimager/dsp/Bundle.properties | 3 - .../dsp/Bundle.properties-MERGED | 4 +- .../logicalimager/dsp/LogicalImagerPanel.form | 13 +- .../logicalimager/dsp/LogicalImagerPanel.java | 113 ++++++++---------- 4 files changed, 53 insertions(+), 80 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/Bundle.properties b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/Bundle.properties index d6cc1be0e7..665b9f3f2e 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/Bundle.properties @@ -5,10 +5,7 @@ LogicalImagerPanel.selectFromDriveLabel.text=Select Acquisition From Drive LogicalImagerPanel.selectDriveLabel.text=Select Drive LogicalImagerPanel.selectFolderLabel.text=Selected Folder: -LogicalImagerPanel.messageTextArea.text= -LogicalImagerPanel.pathTextField.text= LogicalImagerPanel.manualRadioButton.text=Manually Choose Folder -LogicalImagerPanel.importRadioButton.toolTipText= LogicalImagerPanel.importRadioButton.text=Import From External Drive LogicalImagerPanel.browseButton.text=Browse LogicalImagerPanel.refreshButton.text=Refresh diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/Bundle.properties-MERGED index 76b0cf6c23..8928ca6911 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/Bundle.properties-MERGED @@ -48,6 +48,7 @@ LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been LogicalImagerDSProcessor.noCurrentCase=No current case LogicalImagerPanel.imageTable.columnModel.title0=Hostname LogicalImagerPanel.imageTable.columnModel.title1=Extracted Date +LogicalImagerPanel.imageTable.columnModel.title2=Path # {0} - sparseImageDirectory LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain any images # {0} - invalidFormatDirectory @@ -60,10 +61,7 @@ LogicalImagerPanel.selectAcquisitionFromDriveLabel.text=Select acquisition from LogicalImagerPanel.selectFromDriveLabel.text=Select Acquisition From Drive LogicalImagerPanel.selectDriveLabel.text=Select Drive LogicalImagerPanel.selectFolderLabel.text=Selected Folder: -LogicalImagerPanel.messageTextArea.text= -LogicalImagerPanel.pathTextField.text= LogicalImagerPanel.manualRadioButton.text=Manually Choose Folder -LogicalImagerPanel.importRadioButton.toolTipText= LogicalImagerPanel.importRadioButton.text=Import From External Drive LogicalImagerPanel.browseButton.text=Browse LogicalImagerPanel.refreshButton.text=Refresh diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.form b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.form index 43a8e0f1ca..5f91454f22 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.form +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.form @@ -89,7 +89,7 @@ - + @@ -133,9 +133,7 @@ - - - + @@ -156,9 +154,6 @@ - - - @@ -231,6 +226,7 @@ + @@ -280,9 +276,6 @@ - - - diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java index 880259f95a..a35851862e 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java @@ -39,8 +39,8 @@ import javax.swing.ListSelectionModel; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.filechooser.FileSystemView; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableModel; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableColumn; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.coreutils.Logger; @@ -60,12 +60,14 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { private static final long serialVersionUID = 1L; private static final String NO_IMAGE_SELECTED = Bundle.LogicalImagerPanel_messageLabel_noImageSelected(); private static final String DRIVE_HAS_NO_IMAGES = Bundle.LogicalImagerPanel_messageLabel_driveHasNoImages(); + private static final int COLUMN_TO_SORT_ON_INDEX = 1; + private static final int NUMBER_OF_VISIBLE_COLUMNS = 2; private static final String[] EMPTY_LIST_DATA = {}; private final JFileChooser fileChooser = new JFileChooser(); private final Pattern regex = Pattern.compile("Logical_Imager_(.+)_(\\d{4})(\\d{2})(\\d{2})_(\\d{2})_(\\d{2})_(\\d{2})"); private Path choosenImageDirPath; - private TableModel imageTableModel; + private DefaultTableModel imageTableModel; /** * Creates new form LogicalImagerPanel @@ -75,6 +77,17 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { */ private LogicalImagerPanel(String context) { initComponents(); + //hide path column while leaving it in model + if (imageTable.getColumnCount() > NUMBER_OF_VISIBLE_COLUMNS) { + TableColumn columnToHide = imageTable.getColumn(imageTableModel.getColumnName(NUMBER_OF_VISIBLE_COLUMNS)); + if (columnToHide != null) { + imageTable.removeColumn(columnToHide); + } + //sort on specified column in decending orders + imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); + imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); + } + jScrollPane1.setBorder(null); clearImageTable(); } @@ -133,7 +146,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { buttonGroup1.add(importRadioButton); importRadioButton.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(importRadioButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.importRadioButton.text")); // NOI18N - importRadioButton.setToolTipText(org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.importRadioButton.toolTipText")); // NOI18N + importRadioButton.setToolTipText(""); importRadioButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { importRadioButtonActionPerformed(evt); @@ -148,7 +161,6 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { } }); - pathTextField.setText(org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.pathTextField.text")); // NOI18N pathTextField.setDisabledTextColor(java.awt.Color.black); pathTextField.setEnabled(false); @@ -180,6 +192,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { imageScrollPane.setPreferredSize(new java.awt.Dimension(346, 402)); + imageTable.setAutoCreateRowSorter(true); imageTable.setModel(new javax.swing.table.DefaultTableModel( new Object [][] { @@ -214,7 +227,6 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { messageTextArea.setForeground(java.awt.Color.red); messageTextArea.setLineWrap(true); messageTextArea.setRows(3); - messageTextArea.setText(org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.messageTextArea.text")); // NOI18N messageTextArea.setBorder(null); messageTextArea.setDisabledTextColor(java.awt.Color.red); messageTextArea.setEnabled(false); @@ -272,7 +284,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(imageScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addComponent(driveListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 106, Short.MAX_VALUE)) + .addComponent(driveListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 23, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(refreshButton) .addGap(18, 18, 18) @@ -334,7 +346,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { private void imageTableSelect() { int index = imageTable.getSelectedRow(); if (index != -1) { - choosenImageDirPath = Paths.get((String) imageTableModel.getValueAt(index, 2)); + choosenImageDirPath = Paths.get((String) imageTableModel.getValueAt(imageTable.convertRowIndexToModel(index), 2)); setNormalMessage(choosenImageDirPath.toString()); firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); } else { @@ -365,7 +377,6 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { if (fList != null) { imageTableModel = new ImageTableModel(); - int row = 0; // Find all directories with name like Logical_Imager_HOSTNAME_yyyymmdd_HH_MM_SS // and has vhd files in it for (File file : fList) { @@ -383,10 +394,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { String second = m.group(7); String extractDate = year + "/" + month + "/" + day + " " + hour + ":" + minute + ":" + second; - imageTableModel.setValueAt(hostname, row, 0); - imageTableModel.setValueAt(extractDate, row, 1); - imageTableModel.setValueAt(imageDirPath, row, 2); - row++; + imageTableModel.addRow(new Object[]{hostname, extractDate, imageDirPath}); } } } @@ -394,6 +402,16 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { + " " + driveLetter); imageTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); imageTable.setModel(imageTableModel); + //hide path column while leaving it in model + if (imageTable.getColumnCount() > NUMBER_OF_VISIBLE_COLUMNS) { + TableColumn columnToHide = imageTable.getColumn(imageTableModel.getColumnName(NUMBER_OF_VISIBLE_COLUMNS)); + if (columnToHide != null) { + imageTable.removeColumn(columnToHide); + } + //sort on specified column in decending order + imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); + imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); + } fixImageTableColumnWidth(); // If there are any images, select the first one if (imageTable.getRowCount() > 0) { @@ -430,6 +448,16 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { private void clearImageTable() { imageTableModel = new ImageTableModel(); imageTable.setModel(imageTableModel); + //hide path column while leaving it in model + if (imageTable.getColumnCount() > NUMBER_OF_VISIBLE_COLUMNS) { + TableColumn columnToHide = imageTable.getColumn(imageTableModel.getColumnName(NUMBER_OF_VISIBLE_COLUMNS)); + if (columnToHide != null) { + imageTable.removeColumn(columnToHide); + } + //sort on specified column in decending order + imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); + imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); + } fixImageTableColumnWidth(); } @@ -598,25 +626,19 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { /** * Image Table Model */ - private class ImageTableModel extends AbstractTableModel { + private class ImageTableModel extends DefaultTableModel { - private final List hostnames = new ArrayList<>(); - private final List extractDates = new ArrayList<>(); - private final List imageDirPaths = new ArrayList<>(); - - @Override - public int getRowCount() { - return hostnames.size(); - } + private static final long serialVersionUID = 1L; @Override public int getColumnCount() { - return 2; + return 3; } @Messages({ "LogicalImagerPanel.imageTable.columnModel.title0=Hostname", - "LogicalImagerPanel.imageTable.columnModel.title1=Extracted Date" + "LogicalImagerPanel.imageTable.columnModel.title1=Extracted Date", + "LogicalImagerPanel.imageTable.columnModel.title2=Path" }) @Override public String getColumnName(int column) { @@ -628,55 +650,18 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { case 1: colName = Bundle.LogicalImagerPanel_imageTable_columnModel_title1(); break; + case 2: + colName = Bundle.LogicalImagerPanel_imageTable_columnModel_title2(); + break; default: break; } return colName; } - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - Object ret = null; - switch (columnIndex) { - case 0: - ret = hostnames.get(rowIndex); - break; - case 1: - ret = extractDates.get(rowIndex); - break; - case 2: - ret = imageDirPaths.get(rowIndex); - break; - default: - throw new UnsupportedOperationException("Invalid table column index: " + columnIndex); //NON-NLS - } - return ret; - } - @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return false; } - - @Override - public void setValueAt(Object aValue, int rowIndex, int columnIndex) { - switch (columnIndex) { - case 0: - hostnames.add((String) aValue); - break; - case 1: - extractDates.add((String) aValue); - break; - case 2: - imageDirPaths.add((String) aValue); - break; - default: - throw new UnsupportedOperationException("Invalid table column index: " + columnIndex); //NON-NLS - } - // Only show the hostname and extractDates column - if (columnIndex < 2) { - super.setValueAt(aValue, rowIndex, columnIndex); - } - } } } From 04e3a9a5f2cd048992faaeeb80de485f4c8f7f33 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 8 Jul 2019 17:56:09 -0400 Subject: [PATCH 25/69] 5280 fix case where order changes but selction doesn't --- .../logicalimager/dsp/LogicalImagerPanel.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java index a35851862e..551e55516c 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java @@ -346,11 +346,9 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { private void imageTableSelect() { int index = imageTable.getSelectedRow(); if (index != -1) { - choosenImageDirPath = Paths.get((String) imageTableModel.getValueAt(imageTable.convertRowIndexToModel(index), 2)); - setNormalMessage(choosenImageDirPath.toString()); + setNormalMessage((String) imageTableModel.getValueAt(imageTable.convertRowIndexToModel(index), 2)); firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); } else { - choosenImageDirPath = null; setErrorMessage(NO_IMAGE_SELECTED); firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); } @@ -418,12 +416,10 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { imageTable.setRowSelectionInterval(0, 0); imageTableSelect(); } else { - choosenImageDirPath = null; setErrorMessage(DRIVE_HAS_NO_IMAGES); } } else { clearImageTable(); - choosenImageDirPath = null; setErrorMessage(DRIVE_HAS_NO_IMAGES); } } @@ -601,11 +597,24 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { * @return true if a proper image has been selected, false otherwise */ boolean validatePanel() { - return choosenImageDirPath != null && choosenImageDirPath.toFile().exists(); + if (manualRadioButton.isSelected()) { + return choosenImageDirPath != null && choosenImageDirPath.toFile().exists(); + } else if (imageTable.getSelectedRow() != -1) { + Path path = Paths.get((String) imageTableModel.getValueAt(imageTable.convertRowIndexToModel(imageTable.getSelectedRow()), 2)); + return path != null && path.toFile().exists(); + } else { + return false; + } } Path getImageDirPath() { - return choosenImageDirPath; + if (manualRadioButton.isSelected()) { + return choosenImageDirPath; + } else if (imageTable.getSelectedRow() != -1) { + return Paths.get((String) imageTableModel.getValueAt(imageTable.convertRowIndexToModel(imageTable.getSelectedRow()), 2)); + } else { + return null; + } } @Override From 832ac357c8001082e4fc7ec68559b4a4d7be46a0 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 8 Jul 2019 17:58:08 -0400 Subject: [PATCH 26/69] 5280 rename variable for clarity --- .../logicalimager/dsp/LogicalImagerPanel.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java index 551e55516c..b8ed49b3ff 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java @@ -66,7 +66,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { private final JFileChooser fileChooser = new JFileChooser(); private final Pattern regex = Pattern.compile("Logical_Imager_(.+)_(\\d{4})(\\d{2})(\\d{2})_(\\d{2})_(\\d{2})_(\\d{2})"); - private Path choosenImageDirPath; + private Path manualImageDirPath; private DefaultTableModel imageTableModel; /** @@ -311,7 +311,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { }) private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed imageTable.clearSelection(); - choosenImageDirPath = null; + manualImageDirPath = null; setErrorMessage(NO_IMAGE_SELECTED); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int retval = fileChooser.showOpenDialog(this); @@ -331,7 +331,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); return; } - choosenImageDirPath = Paths.get(path); + manualImageDirPath = Paths.get(path); setNormalMessage(path); firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); } else { @@ -472,7 +472,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { refreshButton.setEnabled(false); - choosenImageDirPath = null; + manualImageDirPath = null; setNormalMessage(""); firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); }//GEN-LAST:event_manualRadioButtonActionPerformed @@ -487,7 +487,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { refreshButton.setEnabled(true); - choosenImageDirPath = null; + manualImageDirPath = null; setNormalMessage(""); refreshButton.doClick(); }//GEN-LAST:event_importRadioButtonActionPerformed @@ -582,7 +582,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { void reset() { //reset the UI elements to default - choosenImageDirPath = null; + manualImageDirPath = null; setNormalMessage(""); driveList.setListData(EMPTY_LIST_DATA); clearImageTable(); @@ -598,7 +598,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { */ boolean validatePanel() { if (manualRadioButton.isSelected()) { - return choosenImageDirPath != null && choosenImageDirPath.toFile().exists(); + return manualImageDirPath != null && manualImageDirPath.toFile().exists(); } else if (imageTable.getSelectedRow() != -1) { Path path = Paths.get((String) imageTableModel.getValueAt(imageTable.convertRowIndexToModel(imageTable.getSelectedRow()), 2)); return path != null && path.toFile().exists(); @@ -609,7 +609,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { Path getImageDirPath() { if (manualRadioButton.isSelected()) { - return choosenImageDirPath; + return manualImageDirPath; } else if (imageTable.getSelectedRow() != -1) { return Paths.get((String) imageTableModel.getValueAt(imageTable.convertRowIndexToModel(imageTable.getSelectedRow()), 2)); } else { From 2faa84ec6886f171c3b6205919395fade0546fcf Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 9 Jul 2019 10:09:15 -0400 Subject: [PATCH 27/69] 5280 refresh path shown when sorting changed --- .../logicalimager/dsp/LogicalImagerPanel.java | 39 ++++++++----------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java index b8ed49b3ff..bda577ca9a 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java @@ -38,6 +38,8 @@ import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; +import javax.swing.event.RowSorterEvent; +import javax.swing.event.RowSorterListener; import javax.swing.filechooser.FileSystemView; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; @@ -77,19 +79,28 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { */ private LogicalImagerPanel(String context) { initComponents(); + configureImageTable(); + jScrollPane1.setBorder(null); + clearImageTable(); + } + + private void configureImageTable() { //hide path column while leaving it in model if (imageTable.getColumnCount() > NUMBER_OF_VISIBLE_COLUMNS) { TableColumn columnToHide = imageTable.getColumn(imageTableModel.getColumnName(NUMBER_OF_VISIBLE_COLUMNS)); if (columnToHide != null) { imageTable.removeColumn(columnToHide); } + imageTable.getRowSorter().addRowSorterListener(new RowSorterListener() { + @Override + public void sorterChanged(RowSorterEvent e) { + imageTableSelect(); + } + }); //sort on specified column in decending orders imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); } - - jScrollPane1.setBorder(null); - clearImageTable(); } /** @@ -400,16 +411,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { + " " + driveLetter); imageTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); imageTable.setModel(imageTableModel); - //hide path column while leaving it in model - if (imageTable.getColumnCount() > NUMBER_OF_VISIBLE_COLUMNS) { - TableColumn columnToHide = imageTable.getColumn(imageTableModel.getColumnName(NUMBER_OF_VISIBLE_COLUMNS)); - if (columnToHide != null) { - imageTable.removeColumn(columnToHide); - } - //sort on specified column in decending order - imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); - imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); - } + configureImageTable(); fixImageTableColumnWidth(); // If there are any images, select the first one if (imageTable.getRowCount() > 0) { @@ -444,16 +446,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { private void clearImageTable() { imageTableModel = new ImageTableModel(); imageTable.setModel(imageTableModel); - //hide path column while leaving it in model - if (imageTable.getColumnCount() > NUMBER_OF_VISIBLE_COLUMNS) { - TableColumn columnToHide = imageTable.getColumn(imageTableModel.getColumnName(NUMBER_OF_VISIBLE_COLUMNS)); - if (columnToHide != null) { - imageTable.removeColumn(columnToHide); - } - //sort on specified column in decending order - imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); - imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); - } + configureImageTable(); fixImageTableColumnWidth(); } From cbf012155938f0c46be9cd1c590e3fc58da8b68f Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 9 Jul 2019 10:20:29 -0400 Subject: [PATCH 28/69] 5280 add comments to make image table configuration more clear --- .../autopsy/logicalimager/dsp/LogicalImagerPanel.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java index bda577ca9a..7be999ffab 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java @@ -84,6 +84,10 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { clearImageTable(); } + /** + * Perform the Image Table configuration necessary when a new table model is + * set. + */ private void configureImageTable() { //hide path column while leaving it in model if (imageTable.getColumnCount() > NUMBER_OF_VISIBLE_COLUMNS) { @@ -91,6 +95,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { if (columnToHide != null) { imageTable.removeColumn(columnToHide); } + //Allow refreshing of the Path displayed when sort order changed imageTable.getRowSorter().addRowSorterListener(new RowSorterListener() { @Override public void sorterChanged(RowSorterEvent e) { From d3d73a983b6afaeecec6022c6887a9d2e86c22b1 Mon Sep 17 00:00:00 2001 From: Raman Date: Tue, 9 Jul 2019 13:08:18 -0400 Subject: [PATCH 29/69] 5237: Duplicate datasources do not show in the ImageGallery Path group tree --- .../gui/navpanel/GroupCellFactory.java | 2 +- .../imagegallery/gui/navpanel/GroupTree.java | 5 +++++ .../imagegallery/gui/navpanel/GroupTreeNode.java | 14 +++++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupCellFactory.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupCellFactory.java index 31dbfe3286..c5497d31af 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupCellFactory.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupCellFactory.java @@ -189,7 +189,7 @@ class GroupCellFactory { @Override public String getGroupName() { return Optional.ofNullable(getItem()) - .map(treeNode -> StringUtils.defaultIfBlank(treeNode.getPath(), DrawableGroup.getBlankGroupName())) + .map(treeNode -> StringUtils.defaultIfBlank(treeNode.getDisplayName(), DrawableGroup.getBlankGroupName())) .orElse(""); } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTree.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTree.java index 42fbfbec0b..b51124e391 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTree.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTree.java @@ -158,6 +158,11 @@ final public class GroupTree extends NavPanel> { String path = g.getGroupByValueDislpayName(); if (g.getGroupByAttribute() == DrawableAttribute.PATH) { String[] cleanPathTokens = StringUtils.stripStart(path, "/").split("/"); + + // Append obj id to the top level data source name to allow for duplicate data source names + if (g.getGroupKey().getDataSourceObjId() > 0) { + cleanPathTokens[0] = cleanPathTokens[0].concat(String.format("(Id: %d)", g.getGroupKey().getDataSourceObjId())); + } return Arrays.asList(cleanPathTokens); } else { String stripStart = StringUtils.strip(path, "/"); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTreeNode.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTreeNode.java index 8cb32336c2..9101d89147 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTreeNode.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/navpanel/GroupTreeNode.java @@ -27,6 +27,7 @@ class GroupTreeNode { private final String path; private DrawableGroup group; + private final String dispName; public String getPath() { return path; @@ -36,9 +37,20 @@ class GroupTreeNode { return group; } - GroupTreeNode(String path, DrawableGroup group) { + public String getDisplayName() { + return dispName; + } + + GroupTreeNode(String path, DrawableGroup group) { this.path = path; this.group = group; + + // If the path has a obj id, strip it for display purpose. + if (path.toLowerCase().contains(("(Id: ").toLowerCase())) { + dispName = path.substring(0, path.indexOf("(Id: ")); + } else { + dispName = path; + } } void setGroup(DrawableGroup g) { From 0eed5d00850d3ef865ef6491eca5865fd5a81d73 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Tue, 9 Jul 2019 14:27:57 -0400 Subject: [PATCH 30/69] Bug fix --- .../corecomponents/DataResultViewerTable.java | 57 ++++++++++++++----- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index e1feecf807..af946636b3 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -33,8 +33,10 @@ import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Queue; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; @@ -149,7 +151,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { * OutlineView to the actions global context. * * @param explorerManager The explorer manager of the ancestor top - * component. + * component. */ public DataResultViewerTable(ExplorerManager explorerManager) { this(explorerManager, Bundle.DataResultViewerTable_title()); @@ -162,8 +164,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer { * in the OutlineView to the actions global context. * * @param explorerManager The explorer manager of the ancestor top - * component. - * @param title The title. + * component. + * @param title The title. */ public DataResultViewerTable(ExplorerManager explorerManager, String title) { super(explorerManager); @@ -177,7 +179,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { initComponents(); initializePagingSupport(); - + /* * Disable the CSV export button for the common properties results */ @@ -700,7 +702,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { * order. * * @return a List> of the properties in the persisted - * order. + * order. */ private synchronized List> loadColumnOrder() { @@ -736,16 +738,45 @@ public class DataResultViewerTable extends AbstractDataResultViewer { } } - // If none of the properties had previous settings, we should decrement - // each value by the number of properties to make the values 0-indexed. - if (noPreviousSettings) { - ArrayList keys = new ArrayList<>(propertiesMap.keySet()); - for (int key : keys) { - propertiesMap.put(key - props.size(), propertiesMap.remove(key)); + /* + NOTE: it is possible to have "discontinuities" in the keys (i.e. column numbers) + of the map. This happens when some of the columns had a previous setting, and + other columns did not. We need to make the keys 0-indexed and continuous. + */ + compactPropertiesMap(); + + return new ArrayList<>(propertiesMap.values()); + } + + /** + * Makes properties map 0-indexed and re-arranges elements to make sure the + * indexes are continuous. + */ + private void compactPropertiesMap() { + + // check if there are discontinuities in the map keys. + int size = propertiesMap.size(); + Queue availablePositions = new LinkedList<>(); + for (int i = 0; i < size; i++) { + if (!propertiesMap.containsKey(i)) { + availablePositions.add(i); } } - return new ArrayList<>(propertiesMap.values()); + // if there are no discontinuities, we are done + if (availablePositions.isEmpty()) { + return; + } + + // otherwise, move map the elemets into the available positions. + // we don't want to just move down all elements, as we want to preserve the order + // of the ones that had previous setting (i.e. ones that have key < size) + ArrayList keys = new ArrayList<>(propertiesMap.keySet()); + for (int key : keys) { + if (key >= size) { + propertiesMap.put(availablePositions.remove(), propertiesMap.remove(key)); + } + } } /** @@ -1420,7 +1451,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { }//GEN-LAST:event_gotoPageTextFieldActionPerformed @NbBundle.Messages({"DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export" - }) + }) private void exportCSVButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCSVButtonActionPerformed Node currentRoot = this.getExplorerManager().getRootContext(); if (currentRoot != null && currentRoot.getChildren().getNodesCount() > 0) { From 916f99d3ff2283097236e8a2ebb4fa2a9ef46559 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Tue, 9 Jul 2019 14:53:28 -0400 Subject: [PATCH 31/69] Bug fix --- .../sleuthkit/autopsy/directorytree/ViewContextAction.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java index c5ab6367f2..ef8adfa319 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ViewContextAction.java @@ -325,16 +325,19 @@ public class ViewContextAction extends AbstractAction { * content. */ Children treeNodeChildren = node.getChildren(); + Node parentTreeViewNode = null; for (int i = 0; i < ancestorChildren.getNodesCount(); i++) { Node ancestorNode = ancestorChildren.getNodeAt(i); for (int j = 0; j < treeNodeChildren.getNodesCount(); j++) { Node treeNode = treeNodeChildren.getNodeAt(j); if (ancestorNode.getName().equals(treeNode.getName())) { - return treeNode; + parentTreeViewNode = treeNode; + treeNodeChildren = treeNode.getChildren(); + break; } } } - return null; + return parentTreeViewNode; } /** From e4703974f1f4526f46177fd7290f82af59766db1 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Fri, 28 Jun 2019 16:59:27 -0400 Subject: [PATCH 32/69] 3384 rename Open Output Folder --- Core/src/org/sleuthkit/autopsy/actions/Bundle.properties | 8 ++++---- .../sleuthkit/autopsy/actions/Bundle.properties-MERGED | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties index 8f8b3b4b42..a2feedc54f 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties @@ -36,10 +36,10 @@ GetTagNameDialog.tagNameExistsTskCore.msg=The {0} tag name already exists in the OpenLogFolder.error1=Log File Not Found: {0} OpenLogFolder.CouldNotOpenLogFolder=Could not open log folder CTL_OpenLogFolder=Open Log Folder -CTL_OpenOutputFolder=Open Output Folder -OpenOutputFolder.error1=Output Folder Not Found: {0} -OpenOutputFolder.noCaseOpen=No open case, therefore no current output folder available. -OpenOutputFolder.CouldNotOpenOutputFolder=Could not open output folder +CTL_OpenOutputFolder=Open Case Folder +OpenOutputFolder.error1=Case Folder Not Found: {0} +OpenOutputFolder.noCaseOpen=No open case, therefore no current case folder available. +OpenOutputFolder.CouldNotOpenOutputFolder=Could not open case folder ShowIngestProgressSnapshotAction.actionName.text=Get Ingest Progress Snapshot OpenPythonModulesFolderAction.actionName.text=Python Plugins OpenPythonModulesFolderAction.errorMsg.folderNotFound=Python plugins folder not found: {0} diff --git a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED index 506786c42d..a3a13c0cff 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties-MERGED @@ -79,10 +79,10 @@ GetTagNameDialog.tagNameExistsTskCore.msg=The {0} tag name already exists in the OpenLogFolder.error1=Log File Not Found: {0} OpenLogFolder.CouldNotOpenLogFolder=Could not open log folder CTL_OpenLogFolder=Open Log Folder -CTL_OpenOutputFolder=Open Output Folder -OpenOutputFolder.error1=Output Folder Not Found: {0} -OpenOutputFolder.noCaseOpen=No open case, therefore no current output folder available. -OpenOutputFolder.CouldNotOpenOutputFolder=Could not open output folder +CTL_OpenOutputFolder=Open Case Folder +OpenOutputFolder.error1=Case Folder Not Found: {0} +OpenOutputFolder.noCaseOpen=No open case, therefore no current case folder available. +OpenOutputFolder.CouldNotOpenOutputFolder=Could not open case folder # {0} - old tag name # {1} - artifactID ReplaceBlackboardArtifactTagAction.replaceTag.alert=Unable to replace tag {0} for artifact {1}. From ec3ab9b06671e48fb5930102cbe519e4170ceded Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Wed, 10 Jul 2019 13:10:08 -0400 Subject: [PATCH 33/69] 5280 make comment regarding toggling of order more clear --- .../sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java index 7be999ffab..de87b9aeed 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java @@ -102,7 +102,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { imageTableSelect(); } }); - //sort on specified column in decending orders + //sort on specified column in decending order, the first call will toggle to ascending order, the second to descending order imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); } From 72ce4eaf702dea7d874a04c68f05b1d8a33311e4 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Wed, 10 Jul 2019 13:25:55 -0400 Subject: [PATCH 34/69] minor --- .../autopsy/corecomponents/DataResultViewerTable.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index af946636b3..2bfaa43ef2 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -723,7 +723,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer { * property at the end. */ int offset = props.size(); - boolean noPreviousSettings = true; final Preferences preferences = NbPreferences.forModule(DataResultViewerTable.class); @@ -768,7 +767,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { return; } - // otherwise, move map the elemets into the available positions. + // otherwise, move map elemets into the available positions. // we don't want to just move down all elements, as we want to preserve the order // of the ones that had previous setting (i.e. ones that have key < size) ArrayList keys = new ArrayList<>(propertiesMap.keySet()); From 6e6436dd5255cb3e1d28664e8d53981fed466cb9 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Wed, 10 Jul 2019 13:32:03 -0400 Subject: [PATCH 35/69] 5280 remove unnecessary code --- .../autopsy/logicalimager/dsp/LogicalImagerPanel.form | 1 - .../autopsy/logicalimager/dsp/LogicalImagerPanel.java | 8 -------- 2 files changed, 9 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.form b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.form index 5f91454f22..8f864e1f20 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.form +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.form @@ -242,7 +242,6 @@ - diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java index de87b9aeed..1119b2b73e 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java @@ -95,13 +95,6 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { if (columnToHide != null) { imageTable.removeColumn(columnToHide); } - //Allow refreshing of the Path displayed when sort order changed - imageTable.getRowSorter().addRowSorterListener(new RowSorterListener() { - @Override - public void sorterChanged(RowSorterEvent e) { - imageTableSelect(); - } - }); //sort on specified column in decending order, the first call will toggle to ascending order, the second to descending order imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); imageTable.getRowSorter().toggleSortOrder(COLUMN_TO_SORT_ON_INDEX); @@ -222,7 +215,6 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { imageTable.setShowHorizontalLines(false); imageTable.setShowVerticalLines(false); imageTable.getTableHeader().setReorderingAllowed(false); - imageTable.setUpdateSelectionOnSort(false); imageTable.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseReleased(java.awt.event.MouseEvent evt) { imageTableMouseReleased(evt); From 00481f9a9449866c54e42b5fb5bc2b1f37da0e36 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Wed, 10 Jul 2019 13:33:04 -0400 Subject: [PATCH 36/69] 5280 remove unused imports --- .../sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java index 1119b2b73e..db29bcc5a9 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java @@ -38,8 +38,6 @@ import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; -import javax.swing.event.RowSorterEvent; -import javax.swing.event.RowSorterListener; import javax.swing.filechooser.FileSystemView; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; From 192b38a8f5ea5d11b1ee8d1b523dc503f8a02571 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Wed, 10 Jul 2019 13:45:36 -0400 Subject: [PATCH 37/69] minor --- .../autopsy/corecomponents/DataResultViewerTable.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 2bfaa43ef2..b99a94af96 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -730,7 +730,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer { Integer value = preferences.getInt(ResultViewerPersistence.getColumnPositionKey(tfn, prop.getName()), -1); if (value >= 0 && value < offset && !propertiesMap.containsKey(value)) { propertiesMap.put(value, prop); - noPreviousSettings = false; } else { propertiesMap.put(offset, prop); offset++; @@ -767,7 +766,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { return; } - // otherwise, move map elemets into the available positions. + // otherwise, move map elements into the available positions. // we don't want to just move down all elements, as we want to preserve the order // of the ones that had previous setting (i.e. ones that have key < size) ArrayList keys = new ArrayList<>(propertiesMap.keySet()); From 15d2be24105865590b8d633705962b16c2ae9cf7 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 10 Jul 2019 13:59:25 -0400 Subject: [PATCH 38/69] Set build.type back to DEVELOPMENT --- nbproject/project.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nbproject/project.properties b/nbproject/project.properties index 0014be6b88..5d59189544 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -6,8 +6,8 @@ app.name=${branding.token} ### if left unset, version will default to today's date app.version=4.12.0 ### build.type must be one of: DEVELOPMENT, RELEASE -build.type=RELEASE -#build.type=DEVELOPMENT +#build.type=RELEASE +build.type=DEVELOPMENT project.org.netbeans.progress=org-netbeans-api-progress project.org.sleuthkit.autopsy.experimental=Experimental From 9c8ea7ef9a40b79e19f31f519fd4b407f5139732 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 10 Jul 2019 17:08:31 -0400 Subject: [PATCH 39/69] Clean up of OpenCvLoader.java --- .../autopsy/corelibs/OpenCvLoader.java | 64 ++++++++++++------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/CoreLibs/src/org/sleuthkit/autopsy/corelibs/OpenCvLoader.java b/CoreLibs/src/org/sleuthkit/autopsy/corelibs/OpenCvLoader.java index 3e61fac4a1..0a46afd8f9 100644 --- a/CoreLibs/src/org/sleuthkit/autopsy/corelibs/OpenCvLoader.java +++ b/CoreLibs/src/org/sleuthkit/autopsy/corelibs/OpenCvLoader.java @@ -22,57 +22,73 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.opencv.core.Core; +/** + * A utility class that loads the core OpenCV library and allows clients to + * verify that the library was loaded. + */ public final class OpenCvLoader { - private static final Logger LOGGER = Logger.getLogger(OpenCvLoader.class.getName()); + private static final Logger logger = Logger.getLogger(OpenCvLoader.class.getName()); private static boolean openCvLoaded; - private static UnsatisfiedLinkError exception = null; + private static UnsatisfiedLinkError exception = null; // Deprecated static { + openCvLoaded = false; try { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); openCvLoaded = true; } catch (UnsatisfiedLinkError ex) { - LOGGER.log(Level.WARNING, "Unable to load OpenCV", ex); - exception = ex; //save relevant error for throwing at appropriate time - openCvLoaded = false; - } catch (SecurityException ex) { - LOGGER.log(Level.WARNING, "Unable to load OpenCV", ex); - openCvLoaded = false; + logger.log(Level.WARNING, "Failed to load core OpenCV library", ex); + /* + * Save exception to rethrow later (deprecated). + */ + exception = ex; + } catch (Exception ex) { + /* + * Exception firewall to ensure that runtime exceptions do not cause + * the loading of this class by the Java class loader to fail. + */ + logger.log(Level.WARNING, "Failed to load core OpenCV library", ex); } - + } /** - * Return whether or not the OpenCV library has been loaded. + * Indicates whether or not the core OpenCV library has been loaded. * - * @return - true if the opencv library is loaded or false if it is not - * @throws UnsatisfiedLinkError - A COPY of the exception that prevented - * OpenCV from loading. Note that the stack trace in the exception can be - * confusing because it refers to a past invocation. + * @return True or false. + */ + public static boolean hasOpenCvLoaded() { + return openCvLoaded; + } + + /** + * Private constructor to prevent instantiation of this utility class. + */ + private OpenCvLoader() { + } + + /** + * Indicates whether or not the core OpenCV library has been loaded. + * + * @return True or false. + * + * @throws UnsatisfiedLinkError if this error was thrown during the loading + * of the core OpenCV library during static + * initialization of this class. * * @deprecated Use hasOpenCvLoaded instead. */ @Deprecated public static boolean isOpenCvLoaded() throws UnsatisfiedLinkError { if (!openCvLoaded) { - //exception should never be null if the open cv isn't loaded but just in case if (exception != null) { throw exception; } else { throw new UnsatisfiedLinkError("OpenCV native library failed to load"); } - } return openCvLoaded; } - /** - * Return whether OpenCV library has been loaded. - * - * @return true if OpenCV library was loaded, false if not. - */ - public static boolean hasOpenCvLoaded() { - return openCvLoaded; - } } From db5f605ebe5f4e55c4bd0091684b44a0a18598c0 Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Thu, 11 Jul 2019 17:46:36 -0400 Subject: [PATCH 40/69] reorganized multiuser setup. Minor changes elsewhere --- docs/doxygen-user/advanced.dox | 9 -- docs/doxygen-user/experimental.dox | 6 - .../doxygen-user/images/credentialManager.PNG | Bin 100169 -> 0 bytes docs/doxygen-user/images/getHostname.PNG | Bin 9504 -> 0 bytes docs/doxygen-user/images/hostname.PNG | Bin 38706 -> 0 bytes .../images/multi-user-network.png | Bin 0 -> 103386 bytes .../images/postgresqlinstall1.PNG | Bin 12046 -> 0 bytes .../images/postgresqlinstall2.PNG | Bin 22089 -> 0 bytes docs/doxygen-user/images/toConnect.PNG | Bin 58514 -> 0 bytes docs/doxygen-user/installMultiUser.dox | 31 ---- docs/doxygen-user/installSolr.dox | 108 ------------- docs/doxygen-user/installation.dox | 48 ++++-- docs/doxygen-user/main.dox | 17 +- .../createMultiUserCase.dox} | 2 +- .../{ => multi-user}/installActiveMQ.dox | 30 +++- .../multi-user/installMultiUser.dox | 40 +++++ .../multi-user/installMultiUserClient.dox | 22 +++ .../{ => multi-user}/installPostgres.dox | 87 ++++++----- .../multi-user/installSharedStorage.dox | 42 +++++ docs/doxygen-user/multi-user/installSolr.dox | 145 ++++++++++++++++++ .../multi-user/installSystems.dox | 42 +++++ docs/doxygen-user/multi-user/installUsers.dox | 75 +++++++++ .../{ => multi-user}/multiuser-security.dox | 0 docs/doxygen-user/windows_authentication.dox | 57 ------- 24 files changed, 487 insertions(+), 274 deletions(-) delete mode 100644 docs/doxygen-user/advanced.dox delete mode 100644 docs/doxygen-user/images/credentialManager.PNG delete mode 100644 docs/doxygen-user/images/getHostname.PNG delete mode 100644 docs/doxygen-user/images/hostname.PNG create mode 100644 docs/doxygen-user/images/multi-user-network.png delete mode 100644 docs/doxygen-user/images/postgresqlinstall1.PNG delete mode 100644 docs/doxygen-user/images/postgresqlinstall2.PNG delete mode 100644 docs/doxygen-user/images/toConnect.PNG delete mode 100644 docs/doxygen-user/installMultiUser.dox delete mode 100644 docs/doxygen-user/installSolr.dox rename docs/doxygen-user/{multiuser.dox => multi-user/createMultiUserCase.dox} (98%) rename docs/doxygen-user/{ => multi-user}/installActiveMQ.dox (82%) create mode 100644 docs/doxygen-user/multi-user/installMultiUser.dox create mode 100644 docs/doxygen-user/multi-user/installMultiUserClient.dox rename docs/doxygen-user/{ => multi-user}/installPostgres.dox (50%) create mode 100644 docs/doxygen-user/multi-user/installSharedStorage.dox create mode 100644 docs/doxygen-user/multi-user/installSolr.dox create mode 100644 docs/doxygen-user/multi-user/installSystems.dox create mode 100644 docs/doxygen-user/multi-user/installUsers.dox rename docs/doxygen-user/{ => multi-user}/multiuser-security.dox (100%) delete mode 100644 docs/doxygen-user/windows_authentication.dox diff --git a/docs/doxygen-user/advanced.dox b/docs/doxygen-user/advanced.dox deleted file mode 100644 index cdfb09406b..0000000000 --- a/docs/doxygen-user/advanced.dox +++ /dev/null @@ -1,9 +0,0 @@ -/*! \page advanced_page Advanced Settings - -If you are behind a proxy and need access to a network with Autopsy or one of the modules, you may set your proxy information in the _Tools_, _Options_, _General_ tab as shown in the screenshot below. - -

    -\image html proxySettings.PNG -

    - -*/ \ No newline at end of file diff --git a/docs/doxygen-user/experimental.dox b/docs/doxygen-user/experimental.dox index c5fcc742a8..1eee7831e4 100644 --- a/docs/doxygen-user/experimental.dox +++ b/docs/doxygen-user/experimental.dox @@ -10,10 +10,4 @@ To start, go to Tools->Plugins and select the "Installed" tab, then check the bo \image html experimental_plugins_menu.png -\section exp_features Current Experimental Features - -- \ref auto_ingest_page -- \ref object_detection_page -- \ref volatility_dsp_page - */ \ No newline at end of file diff --git a/docs/doxygen-user/images/credentialManager.PNG b/docs/doxygen-user/images/credentialManager.PNG deleted file mode 100644 index 21acb8779b41c9b47156b8378db3dd7de434ba26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100169 zcmV*VKw7_vP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D|D{PpK~#8N?7ato zB~_I^-f41TKyuE}2pGVCS-^m%n7jwe3xiJNV<*%n)DF>l_y*|TT&{`ki~ zzW3gHmn>QGz3+Vw>-9bFdCzNJ^BR|J(V|6*JT~O3+J+O`Ms)38Z=v?~_Bz!aI?9j9 z;8?wPj}3ON*Jogp=qoj~!H#W5Ov>tAcZ)W~Ti)`PuYUEbUYQImSg_#eqmTB=gz>d^ zzxBGn|EvrC_q*Ty?&m)DIn2w`p7ykDx7~KrO*ifR*0;WO|NZxW=}TWa z`Q(#d^{Q97c#9S-9&t!7NR_*DmshNA#F}L~S{R>;@tIv0S`%rF7>8e=OXDflkxF@u zHE;?H5K@%7BDh0Vnh@$BBIOz z(uuAV=!h(266!Q3aG!Blc8 ziOMpqM$}a)-6%PUun7gcUImxvVsX?=M22`q4GRKMrBMnXK0_CY6E4s8`Vu06$ zSS=6@Wm=6y12Y2Ej+~7hqsU=LMtX*a@LD@GG%`5UGX!GI=+K&;p}~=%!Jgs4k>SD7 zVGzS3!^5L0%P;WLCZnMO^=M%f)@XALUZaEb1&{DMGQ4)Ja%zk%(&$&d@|7o@bkao^ zU3BN2clO2+~@#&xkKyxuj&rv;2pYuvCn0k2oVCDw0X zbtqra3!GRjOGXrAP)4GXX@t>67}BP7fpt3Ob;dQ-WAj_7Qpp@E#$;sT?iw)hx$e>e zWJUz9C#$YN6v~J~Q$%D&BCSSr!&#q5hhpLwK?*y96n7A>(ZRtHyoQkM4zC>=M#)yB zxy-7EMwMqn)Y9U@3n_BiB$_Hvr>;s^qfJ>hUnt=hsrKl|um{xBgEab8uX+_m_|{u* z?cIO>{onoWcegooix&R~h#PC=)oUEvZZ|5;GF>_?i*;126j&Ep<8uUzFPIeT$4ePw zRjFKWDKpYWq-A9_a*Qy#jsQ{`C{!mA>upBLGTm?r=u%};ECbD2XhpV!G9p%8qGX{^ zEyDyRKHXhfFtM~O6P49+S*RA8fRr|L!zsX?<@oZKzdVFYv>qMpMD+LfFIcePm}8C^ z9?p_Z!_St8jdg`gnp)bAkQscI0(V_*=@yZm@jbxYCxn@+#G%MD%PP)5}1{(yL zbvs%HCPk(jBV}~2Re23gnPoaH3)SYbk^t)H$;En`j(Wn9Quo2{9?z)mm7?vm@;L`8E2gF z*0;WO&6+jN3V7q2-}i(6KEo?jc=;=j`R3P7@rnRzUw_kUUVp-emi*-sxVH!fbg)d? z^J%_f0?p|@>inysFPMGSj`M$c>yngn=3)Cm))HkXtd(u6I&;)hYP8PYecxl=|NgUo z`-k*{0fz3t0}r%3zD0``|DeFNAzou5uw8b!V?MkXU!0}|c)bdh0+q~e(4aMLn-Sfp zn-{?AC3y1~BN>NE9m5K`Le;~OV=!4igIuTUdx8`0x`T|*)zA9g+$qGB6Th# z&}cQuSPsF0nWD{Qe90F|_#Ila8vQZ#2wPXoq1W?*ZqcH}27n&}zPf8_KJ(Ky+U=6B|Cd8Pc($OM}f~ zimNEltWt?_s8mAXWN>Z?-Yj4lOk^dDN!^4aj1!wuqWf~(Xi!JG-woDw0f@m7;PnLV zOj&H<YKVClQ>eg?GDcLQxz$jR>1GYonJuiYh`MDK(%Q1E8ROHWX4&9=~ zKQ7V}zjEgt>(0=P{J3SB6(`ba=+bFNQ{(eBKCJ@Qn~`QTTj+SzkJUP&`?9W@qRg2@ zOv;P^Wtj%8x~67Fm95<~Bf3Yjt`(8YFhgC7DNS7^9Mru6lIdmV==;0A?!qd{D zJ=-68#M?jliQn9}yqrU)?pm~H(PBcV+_)zTFdpHouF;LwILuaJvg~ZUXdVG%B!#4; zz=?Dn(R~>at02lv1NG@h=}~UGOb3U z%uG?{bOL5A_d){wga1CG;dRTc$dZd%dR17{tB21w>kB}TH^*1XFR9oIZ>GmwT6pnx zv7z3-B{=-Gx0FShcp%FNQ|jWtA>aji5q6$}E&Aks@s^2z0|S=u1Z| zZOmTaFKL8SF$lRAty?BqYC?bYP%&%9Q>YQ+sDk?VB;Jcg8f7 z{WGWc&6wUhV@B`vnZ48a8l66~XZnnu8U8wN#!i>?Zyqfl|DI170S@?Rjgmc3oH4U! z+RQ$fnt>OX)B9#l?+?D{cj`b-f4{;jqRpXOwD^aHu1DP6>R)j^?#h%e5F^au>TVt z{etH|%PRt`o%zCpk2>YFo6b8M4){DrPtO3}dH}kwm){A^C#3tq=d;n^o6I>&Xa3=# z>1O09vsNE5Z{?4c&irG-dC9z$_C*`Rw1KjBWW*ng#>Rst494&X h)fvpaH*4y6w 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 diff --git a/docs/doxygen-user/images/getHostname.PNG b/docs/doxygen-user/images/getHostname.PNG deleted file mode 100644 index 9497077beb4f59839db0b47f2d676cfe58b145ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9504 zcmch7d0did+jdQh)6{6?G-jqw+31tnqEsfbWSUlEE+fd0Ta6+urk0u_DpOjvSY|~^ z3TWn%niRCTlG(T*LY7PJTYxJlDhLXEH>Q2x`@Hjhzwe*#`Tbnr=DN@Oy07~@&ht2q zi|ek=jz9je>4zmtmi&0+u>J2#mVD<5yngxpO5pFEsY-9)-_p?E9S<%k;%puQ-Ym!3 z9(6+tA{aUj8~%d@YA4JCOsx-Yk*HKzAx?aLm{ zK=GWzU<@e?m|_l3KqV+eS}C#w>J1_^oo9HVO$(aMcm-9}rFX!ZS(dJ2Cw z;!+3B%t{h$N1KFQVP(Er(fz3bSWvd5AuM`Eq@_K5$+-0oY`zo!7@>AM`DL@rOVUoA zKIpis-ou~#p|PI6;nQu_u~O|wl$9QE(fZEr?ry`GxLfiJ?M)*$@{`(Cc*30qBIh{K z_5)m_`*k>>#XTzfIY((v+tbp9GI&=U6hMZycwpyeoMb3cZth>^JZ+0z(b+hbXn5wZ zfO;T_VF4mDuYA_UqlA~dAJ=DaNluO>Yvk#5Vaq{ikJGlzPs?^x02#q}Uz23`^8Cr$ zbCuPlNo6VOBq{oU*v_v#QrzgWCtqC)U+DNk?$I zdPJhdL#e3eY}NdLQ{5FY1wX2XZp@NR)EK^8NJE-W`NxGcj0W;`@kCL@tn)%j4rsZ~ z2fJAoIdO2|*kqL!{M2IL8DGB>>bQ*RA>YTcSrPL$M5b8MJE#zdXGMgJvZwDk2{4t$_sM<^4mbfxuAcK(r)-r41S}Bu$Fx0 zS_R6`lJ*1FxzMEbsAhawP||G8$XoBO z^wT612J|ZIRR|~Mc67yW<;okOqMn%G+URU-+l|7kDRWPYA^Vk~U7Z$nv{A3RqTk1g zgg6f9d_*|qS$0)=2Yh{WjG&_BsUlQJTCcJ=8JqW1>-4LM5Aa^e8^UO>cR^^)8Rh#2 zoL{ZwG6VUv=)7ta|N9`B%V3%ArhyAl=oS0|=LaMF+dG-k3Zp?`u>dqt6a>VM17bgf zQiJP@ph80%SX(F_KYP9IBn6E!m`$%l%ks>UVmQ5Q>nn0T=Qz92EJ?Adv&x#|@#eRDUTfv8USrIR|KCYM#o)a?27V+VfCuK`O+9lF3ewG2YDF*=5 zKw|s-{5T(?O{0vREwwP^s-y05)rAFTjs&+E>f6iC+y-f*iMGN05n&1^>gCerq7A$9 zEC-*ksq(I_E-W9rD8XWGhBaB$rJ35?xx*$w6!gkcBnR+INFTlNp;U&P>YoI4c zM^?JY0&DUO;tfeVX}$V4cHmt?M4h@r^sdgvX+MW|X>Z9v3F1;s`d!8FKuwY~;q(z6 zFrRh2=RgtZwTdXq51u)SJ%#~4$|}k0Lz0^lj}NBz;jRpteh`>T8Kj};6%lmZRW3Xb z-K&*0H@;Ewyee>Lxwln-addOrL1_hN?n7hEJqg z%hxZ6dK;TUt1ZC`ArTJm{17W&Ztp~v-rskn^bG3MHL;uBvsMhe(lEnW2EPV6d+j6I zouoM$LMHkN`srp#Ps+4C75ONf30SopYfR1<5f`TVgG;30{Dj*+ z51!Tnbu0yAmZMKhyJ#|A6chlaRziH5ZzG{6nDwYUq4lhDh^kZf2_)cGy%5JS*Y=-; zMI6!t9D3iB3q#43xoH(@$<=y`j7U>rZQ^N&_{d zNfa~{htML|x-5#A`X>ppj8vsX=7g zTjypjqzQR-FR^a0qziJAZuEjRFli84^O*Kxd#5)U?Z-?*tKQovwp`c)vslVmK6rj# zfP*X(NIlRe*$tsy^r=MFWam0rpa%ZvP0}>S&X}axc?oYBvx^EkO7%Y|SR>EF#@>;A zMv_>b(?nQ9CZ!>7y9?%i&pOfBSOUWU;VrSgH*`3~mI@->K4DyMx+AZI`1)^ZfaS9+ zd8P$A=M%E%yNHma@+F`RsnjVDbNJEgzeRa+qSckzvVuuuj+oww?dxqavRcdP z%b-GCPVEM$=aLd5MBS(Tu>HH{+?Y#mcLxJy&1;h?5dVH|B$v9_xoN&k<*0VhFRS&e zFGPe)DWSELAE{8+;@gEar3|l{$GP7cbb09hGooKB;e^M^H6IP-6ST?ozBR)Jjy<01 zxK)AEc0C7MM8ZZ{wb!q^e9PXz$MpvCtXRKF48oH_>nOhg&3o&}zo7LVPT=H9FRKVA zae)o0%}I^6u4#?|gw0;25+>|{IAx@nw+HI}DyYc2BzHZUN;nH_F2R+V-1)ycvwq@<{H zBqz0CU`qAQMt|nmB@q4xjz9Scu#P3sJO-*E@^4@jwZT#9XCALE1gRGhLG9$Xr6r_r zUbwm#sv)12c`>~-q8$ZXE1$E$d*`HE&Dj+1>=6$b9=R`TxG)mgl3LK8YeksDXXINQ z;b9)K5a1}Jan=X|hHp+pv7KtcN7a;tI!LSr&$8_Hw>A_qYTAd{B|yu~mBf4CqPzWs zgYquOQX8Z%?rSX#oB#?oxBz#wwIEuOAL!6>GA4W<|BQ^6;;W3dLjt$)cDgnwm&+11@g7{)r{aT8xv{mT>j1OpP5<1 z44o4J2b&LR*}Cv>0p69mqg&8Er!5$;>=6_U|2^LlA$s40(w#~yJ{}a?C1758-Ktj~ z>^gaWLgee0SiBw}cp0}=^e0re7#av9w{VB&W?KQibf=@k=G;8<+1LQkDfq1KhfC&x zUuwH58)v1z;P*vK4*V12;PmB{r!4o4i_#}EbsR--x2}Ofyjedk^=a&&XRW*o3hqfQ z_%gM0IIP#I+AaA0Z==v*1HoNr%11`vp%%9&;JJlujVlfkrhY+16wzyxMpW8mYD_rA zawxD1n%z#ADhKsbA_>vAIoM#cHq90k*01Qnu8Cz$enylXc!zoVonIA2M4qS^_6On& zia*69r}>kfmiKllDdUW=$@m@Q@;b_vDK_(`E`=F$#KYdIqf$N`$Zoz6A+4ca8t&d^ z!Qix+Yx^6Fpm%1m5_Bq9f96bn2R!?zf8yw^6}89cYc4ixwuCHDft!XQnhX{|8Jxb5 zOxU zjFJlgcY|p{`_GXsRwCPoq-Nb!NkC;;JGupVG2bQiB2sg2nV57PCIHdb3`hQL&h;F; zh^P>UXj1ziXAi5%6A{E8-mgfjb3{3i-Z=eE zn+ONfBy?Rsp1wdxmZ-0lA9^UIKDue59fKMwA##0)4S8TQAL3qqe&i&-L6I4C58;#Z z#3?%qg|RGg{#FjMrjGiN71w=E7cyv&_|Psk7!NS9LNi(%{*22hVvPXGzg^VRp#98! znwYRjUEpee)(S7nXFSXeD`#P8tN?%guL*{t`6y&p^SZaC+CnF-v*xr@`wGQBy>Xca zGwYpRCe~VduZ-dNgFj+E2%K_^QtRkPSLX-u&P{NaMmP(TWk{X!9kGthSaqq-ziDup z!kT24Tn2r%qnCaBGa%+@D=k{GmW|2lzW#(}Ij9@@5<-x8>D?Tit`_SxK`>tt86V74 zc<8NuuXXDw9KspXo4hk4r2+kQ3n1`b?wi*xiTtUhH&i-d|L?7}HN(eIAo{y;)&(Km zFYmq)X%J9gDF2xr8%#SN$F2b{y5M&Jq(Bt}^zl7s|J|(( z`uFCBIY`N8!k0eN z3CdUX0Q6yUGbDQi8QV(U z-Ik0w$c^?Zx(jH28BiG3#NK<)j`t1U4b%nzK!&mY^w6(YavmHUz#VN_r5`gd-^~!3 zFL#Vng1X~`1_hw?23eMQTRz+6k0PkM$MQFyiR6K_V>noU-KL2fXv*skV|mfMzRj>I zT+1D{Ba~x`Wj9ZZ(3pU1|Nq-us}g#ITFq&X#g@k~ruyZk_Zq`ZspV(p>4JVXYSYN5 ziyF2uye;V?0WEEQ=fVTjMZq0^umA!RSpX_4i1HUXp9WF4@G}mtBEPMkx`!K$JgWff zg}wv~2<3kgu<5V^NBok@yZ?xddzhO@P;CPIh4H>BjdSK};d@>w$M?SuX501mFsnC) zUQu)BMeWZdsM=JQHT_Q-ISMszcQZBG7<~*4)7Iqa$cP6csZ&kL5BFOuyE0hMBQzby z%D2sIY8n}wE8icby8e$mn0bw$b^oEXe?2~_q^8)Pn+<679$eF`~pbCf$bAtjV^Hql>K7S0x`(hCH#VbMGkCmB1uKP4YBVLQw z677DZ4MRvE`ep#%*u5@8QN*5b6Q_&*qz@o9rJ2^E3AJXbd##SJoo0|4AT!|R2E~1j z#x@a){qAdD{Yr>S4)R<(z7q}cp@YKbV52?@)3{BSp^sV4)2lueu`;isV4y}pw8H~i zeOf9Hx4P>#3uVCR1Ephstcz|MD7;YZr4KkyW$^K|@34}e((AzP(0WXm3}{*p7n?a9 zAXKaL%K@)uEU)v0&7}xHgtA6@yNf};{*^TXw5WS$y(+0B=FMihT)}5t*h?r-n z&FG2zLJQR2^(njl17HQf^S9oksCy??PO}AxVwV$F87%i+So6r-PEfPu*7`8rsee+e zaE(suVn;s88_-4iNGhOLwexpIeIErV<&hsaC$#FsYJt4nn<;Ly+ntgbWs+0D$^gpT zde!$LFG-Z2?8h?{OQqs$q6O;RNGtr5-=&$%=hF!_#W8*c>s89V)v0FNl+dr{T|jNe z>)=JtPt;xhX~FaRKd>AW{B1x)M8u*I2vFxU3t*pH%2E^RfORU}f2obc)I)0Sz33@U zxB4!|hpXloiw(WTP|@=aq&5)I{k-}Vs-pGFGGt!a+1|lc)g9lSH&VMUmHoxw=vV(p z$APPui|1sMv&#s64Hb<6a=+eA!NVyoP62RpjWUs6mW$1)%k0p z$n%CP=c`VZr}R{4Rux4kf$|rZii*&D&K@@}u?pbKyhImb&+Z!=3AJ_Odqi z@pS7?-+ZxuBg<+VTyLgFrKnMyl;1+CjW3fh@{^>08SXvwU_?$f%Kk6P<%oasm0xWP75BlL|KNk$~ z)zMc*VN?GILf~=Q&5g?wW~-+3a#_zkFl(pG_S58f&wCmQx5Hfx&U!n@E~))O6NJ75 z;c>da-{!uW#KIfZrV=qBq$mxIO?uf3tgo(L;+9zXP_9eG`G6jXwRT2LTPlfzvdIJpsafZS{K zwmbM5R5B)mn2j?;6UTqjnwH;*Jcoy`7D4(Aw0EBGNr*aU(%+5mQLU#94JvepU!IF8 z|KxqNBQ8tg99fG`GvhsG*@2ZO~yw=EU{2e?gq20+sIg*A}92P_oijxmpubg07wJCkVIZI zd+t7li3#Vt_^nb3>qq}-@~+zr^a#=-p(Fu&7_^J@*I;aW_FMcwI1iT(iHbzZoT!iF zg=0_7KgXtQ5&Q_p%WgiW07*Vc7 z^s@~@i<0up&vvm3`APh1vy;w%r&|XoyglcG%Ty15N0FJ;+TiJi|HGYhJ)+;MC)FF+ zKD)JVr_;0^wY<5w9zpr|SrpJhL{*Z@0k@TKgg%>4!E1Yy5Xu{~Fe2%ch|!+pUB@n} zKD>tLNkfXt>evs(N%mdABOs@U%xW0dEN|yoz~?fE_r(A;b$X<={mm~kdN#8S4KQq@ zBd`mFSQ-9U+~}?>4x2Tj?j_oK<5Iq(UZNU(vO(-hL&I5Gh?RRK^fh?2@YJ~VjFQxS z32Iz_g3ur8AalGdg-LZ0hEjSGmi?$@oxbtp^U_={`1k2sZSMWL|g6w zADqkOd^vj}5x2E0YFZ4)gwfnNK1?j5id!*Pp88fnD`1`?=t;V?c2-nr2ltIoWhr)D zIvaVb+G|CwO2>eisaUB>KP!J2hbgAD8 zv$qC!({R&ldR-aNPQ!UCfD&@qG|opu+N8<$_pvhSd+KkAkj9J8YXXLHjWru_LSw<3 zG*)ZgB!y-(+zsGpujHB7SjSfQN8r$r95KQ{R-L*!RBn|#nf816ky18mGnRt%IL*?! z4`^{~oqq-Y>WcwA(*_Po)=$P4#l@y0W7QtQX`gvlGMI#N^=?7rd^Z`1w6TQlf2VaU z6YSd_n238^Mo=laR_1ZGEw4D480=mEya!}?eH%h8jQXCGi8m4(dVMa&Uc()1T?My9 z|L>q?RMKFma|q)ZeCQM#i*agkXXPW1tj9W%v}tfY)eCxlS-uAnXw?+?HV{Zyu=a^b>1>P!(pCOvtp-idJ1x-GFmFguEeMNlK{ zU6Z#gbohV=#IVYax_6P(tV{BDbC1pVDsKXA0cyuJ{B!9P;3wwO_8fZP2k<<=+A`Hr zi^m7J70nnxM(s{Qy)^n1mld-jqT@OE&cEiS$6Xz$1qfIu1`x6$9&XP8Y-s}^>E>N_ z@K$b~c^0^tw0E!QdV9lh_V;}2DxZvKwLb0wEbB0PiW}M?JRfxSO!vLd4M2x+no;kS zl3IZm+6YxV%QD}cjCKcte>bzNDHkxp<{d$!>-auJ2p@Fza1J%D{raX^$O4X#%v=xU^@|S>F&*` zpWJ->A|G;vpbH0b_B!i!vDBw{bH1jqv#Tm$Jqe>IawFCorlLmi2_pi<4Mq) zdtQ^4auti*CKKBriv%cBX=D=EaEK_k_v%W!K9GuQ()J*mvViZ9HHx%P@Lt!EO8 ze^Hla*~bMM)b=CcFUxsS{(d2~f1c1-Aobs9{{K7#7CV=>!263!OTTG}?(RjY5EK-& zxLd}Y6W?59TMcPei@HMXNLp`}`I^K3%vD2+9XGDF)|L_8qGfLxchdrHsgD}$AgE@0 zc#B_EQP0>S>8?uFp-O}|**jx$ZJZ1JzmC)=DC&J9W)CpvSQ>s4Fs|AwovGyQDveDg zjkZt5&Y!Cz2qw&?%^h$*T?j%3e6&&@#5F}LrU8wuJdp((jISWHOO70Jwl6w( H;`09jRa6ys diff --git a/docs/doxygen-user/images/hostname.PNG b/docs/doxygen-user/images/hostname.PNG deleted file mode 100644 index d97f78ae427d8f2059d1296d0b994d041c4798b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38706 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 H;g#|qF^mwX diff --git a/docs/doxygen-user/images/multi-user-network.png b/docs/doxygen-user/images/multi-user-network.png new file mode 100644 index 0000000000000000000000000000000000000000..bdc6ce7ca714b74134ecca4fdb2246ad02ad91cc GIT binary patch literal 103386 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%0DU7Bl;a69h~d^us4gO7nt#j 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 literal 0 HcmV?d00001 diff --git a/docs/doxygen-user/images/postgresqlinstall1.PNG b/docs/doxygen-user/images/postgresqlinstall1.PNG deleted file mode 100644 index 4223962bd40e75f003d612906839583e0e5930f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12046 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 Ist;cLAGEtnH~;_u diff --git a/docs/doxygen-user/images/postgresqlinstall2.PNG b/docs/doxygen-user/images/postgresqlinstall2.PNG deleted file mode 100644 index 7a417966cafbc3f71de1da073d7dffce8e99e2e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22089 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ÝZ)J700th#!5fS=q`e;Jh30 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 a{}!=kq;&r}@DexpLGKIO+qmag_J0DI>{|N( diff --git a/docs/doxygen-user/images/toConnect.PNG b/docs/doxygen-user/images/toConnect.PNG deleted file mode 100644 index 4bfecbc4b62cdd65babb58f8e80e1f5969940a34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58514 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 l=(OZ!7g!U@blNxX zxBF?`j%I%tO)yv|F(mE&ssD9y5RDc;gh+%tR|$m!aXLZJuCcvsJa;bo8TXs8CJ30* zw^vU4`y1DOt!(5c3e34_yI)_Pas^m8*Orx4cw$H~2C-J$||>s;7~f%zjiuQ7XBfHnfqnM0;_$5 zs#L0T%4xB6MB=7BS6k(#NmV33F^c1PhtI6#j1kIg%((}VDNv{@fA+wD^c+htn9T&u z9D_)lW}9hPnG`?XO&5HsTPO?ywZGheF85m zEa0IbO(%MiCww4|*3{BMfrX_fY`nyoYJsgt{_PYCFI9RaomU(^Y^C&IA}SeB*9f_h z_kvzWo>ItOYisNBvaX8!rmpzy{=ork7a=DH%cFRAC)UWz5Tik^ieClkguj2|4^E*9 z497OVpjLY*VOV*nA8F#fO}>r{lH=j!B_bn>{$UK`sb1$mX5sywZXNQN#i<(yyh5N6 zXwT9 z7_KQ~lBE*n_xJZ9+=#*h@?bFc8NQB=;lB{VMhlRFa>NeO;T<8(v^#(W6B{uesHZZ_ zG^z3M&}uklPS)YfiOfmN_4I-+uJi%H4D@PcJ&v`XAWewM$Z*%}aOK|=fQHa6DwL0$ zQ!UOF@ULip*iP@ltsbdKVQEzv85xWl@(N(04vjW-qO6xC_kVV*`T$NI9;DU0cr9+zt2@X;rJA%BE&yQ;U>#m> z{e6FLZ|~rZI3ckplV4sj8VPI=?YO1GZh{778UYY6ES+1wd|GBh4U8C&rVCRrFf^p3 zprBaf;N#=s;^N`w&nq_S>*?9r+M33@{0N&y$k1R<06}uHcz6)rM2MKN2~3+B#+rr| z2UckCHbtmZeG-az@SgTUsA4>vklZ3=V`D?~#lIOHvNq)Oqf`;@7Htgs8&)9l`+{xd zM^x9iEd8E6;Lp zn@`2mB^T*&_`r_DQ#kmGp?>jC?YDOJIM8=^2s^*;_1WY5ENiN&IH}WFi7_+NJ~9zT z(yP5q5w-qp|7m0oqev3o%|Y*_HJ9QN_ZbeR+xb8sJWoACD22#0o;)0&)M(RTW{um= zk|*Nlh%ur21hAfZJb`D=Zw}ihebo&vA6tb>!O@r9Do31$XOeYvyx8sw54;|Ve}x(L zplL&5X^}J?26iS7zc5a72r4N&@!S>sCqK+<%$`sdFc2-)j@D1 zvR=zZdgS9+V*T>vN7>)fZYebP3pi#3wShO{R5z+*RFA&~lTqM#6r-c5fIWD$eu% z($B)7I0|_sCHTcVP-tWyfmVe1mQli-3?06!a+xH>ctJ#&p&jF<( z8E5b9ZSxB6_ObB&oJNBlE0i~JIh+>ZO32z~pV0^^E|eW-``e4Vh66t$Hc1)SZDhhz zAtj`yx*tx{z&@XB5)u<52*7Ihm9{^%E-dgRNUJ3O2-p(aA`dd69ulADy`?+mwQWU4 z4)s7e=bDp*pFD&erDMcVgl8EQ>WU1Rp)3+CDCuoP4TYSgYz>94?d>Ql7;ll5-lQ`S zUaIpZ+O02Kz*}sgBkDemV1p*_@O_AUp$rRqWq$#;?D?%pfr5xt0mB}4 vqhyUa6hx)@A|jpQJTM{ktSpGM^vhqo%u9`cCVCq<*zbe161ZB@IOP8Tm(IDj diff --git a/docs/doxygen-user/installMultiUser.dox b/docs/doxygen-user/installMultiUser.dox deleted file mode 100644 index 38ec15c79c..0000000000 --- a/docs/doxygen-user/installMultiUser.dox +++ /dev/null @@ -1,31 +0,0 @@ -/*! \page install_multiuser_page Setting Up Multi-user Environment - -\section multiuser_install Multi-user Installation - -Autopsy can be setup to work in an environment where multiple users on different computers can have the same case open at the same time. To set up this type of environment, you will need to configure additional (free and open source) network-based services. - -\subsection multiuser_install_services Network-based Services - -You will need the following that all Autopsy clients can access: -- Centralized storage that all clients running Autopsy have access to. The central storage should be either mounted at the same Windows drive letter or UNC paths should be used everywhere. All clients need to be able to access data using the same path. -- A central PostgreSQL database. A database will be created for each case and will be stored on the local drive of the database server. Installation and configuration is explained in \ref install_postgresql. -- A central Solr text index. A Solr core will be created for each case and will be stored in the case folder (not on the local drive of the Solr server). We recommend using Bitnami Solr. This is explained in \ref install_solr. -- An ActiveMQ messaging server to allow the various clients to communicate with each other. This service has minimal storage requirements. This is explained in \ref install_activemq. - -When you setup the above services, write down the addresses, user names, and passwords or each so that you can configure each of the client systems afterwards. - -We recommend using at least 2 dedicated computers for this additional infrastructure. Spreading the services out across several machines can improve throughput. -If possible, place Solr on a machine by itself, as it is the largest RAM and CPU utilizer among the servers. - -Ensure that the central storage and PostgreSQL servers are regularly backed up. - -\subsection multiuser_install_clients Autopsy Clients - -Once the infrastructure is in place, you will need to configure Autopsy to use them. -- Install Autopsy on each client system as normal using the steps from \ref installation_page. -- Start Autopsy and open the multi-user settings panel from "Tools", "Options", "Multi-user". As shown in the screenshot below, you can then enter all of the address and authentication information for the network-based services. Note that in order to create or open Multi-user cases, "Enable Multi-user cases" must be checked and the settings below must be correct. - -\image html multiuser_settings.PNG - - -*/ diff --git a/docs/doxygen-user/installSolr.dox b/docs/doxygen-user/installSolr.dox deleted file mode 100644 index 494e24588d..0000000000 --- a/docs/doxygen-user/installSolr.dox +++ /dev/null @@ -1,108 +0,0 @@ -/*! \page install_solr Install and Configure Solr -A central Solr server is needed to store keyword indexes, and its embedded Zookeeper is used as a coordination service for Autopsy. To install Solr, perform the following steps: - -\section install_solr_prereq Prerequisites - -You will need: -- A 64-bit version of the Java Runtime Environment (JRE) from http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html. -- The Apache Solr 4.10.3-0 installation package from https://sourceforge.net/projects/autopsy/files/CollaborativeServices/Solr or Direct Download Link -- Access to an installed version of Autopsy so that you can copy files from it. -- A network-accessible machine to install Solr on. Note that the Solr process will need to write data out to the main shared storage drive, and needs adequate permissions to write to this location, which may be across a network. - -\section install_solr_install Installation - -\subsection install_solr_install_java JRE Installation -1. JREs are normally installed under "C:\Program Files\Java\jre(version)", so check there to see if you have one installed already. If not, get the installer from the link in the \ref install_solr_prereq and install it with the default settings. - -\subsection install_solr_install_solr Solr Installation - -The following steps will configure Solr to run using an account that will have access to the network storage. - -1. Run the Bitnami installer, "bitnami-solr-4.10.3-0-windows-installer.exe" -2. If Windows prompts with User Account Control, click _Yes_ -3. Follow the prompts through to completion. You do not need to "Learn more about Bitnami cloud hosting" so you can clear the check box. -4. If you see an error dialog like the following, you may safely ignore it. -

    -\image html apachebadmessage.PNG -
    -5. When the installation completes, clear the "Launch Bitnami Apache Solr Stack Now?" checkbox and click _Finish_. - -\subsection install_solr_config Solr Configuration -1. Stop the _solrJetty_ service by pressing _Start_, typing _services.msc_, pressing _Enter_, and locating the _solrJetty_ Windows service. Select the service and press _Stop the service_. If the service is already stopped and there is no _Stop the service_ available, this is okay. -2. Edit the "C:\Bitnami\solr-4.10.3-0\apache-solr\scripts\serviceinstall.bat" script. You need administrator rights to change this file. The easiest way around this is to save a copy on the Desktop, edit the Desktop version, and copy the new one back over the top of the old. Windows will ask for permission to overwrite the old file; allow it. You should make the following changes to this file: -
    -
    - - Add the following options in the line that begins with "C:\Bitnami\solr-4.10.3-0/apache-solr\scripts\prunsrv.exe" : - + ++JvmOptions=-Dcollection.configName=AutopsyConfig - + ++JvmOptions=-Dbootstrap_confdir="C:\Bitnami\solr-4.10.3-0\apache-solr\solr\configsets\AutopsyConfig\conf" - + ++JvmOptions=-DzkRun -
    - - Replace the path to JavaHome with the path to your 64-bit version of the JRE. If you do not know the path, the correct JavaHome path can be obtained by running the command "where java" from the Windows command line. An example is shown below. The text in yellow is what we are interested in. Do not include the "bin" folder in the path you place into the JavaHome variable. A correct example of the final result will look something like this: –-JavaHome="C:\Program Files\Java\jre1.8.0_111" -

    - A portion of an updated _serviceinstall.bat_ is shown below, with the changes marked in yellow. -

    - \image html serviceinstall.PNG -

    -3. Edit "C:\Bitnami\solr-4.10.3-0\apache-solr\solr\solr.xml" to set the _transientCacheSize_ to the maximum number of cases expected to be open concurrently. If you expect ten concurrent cases, the text to add is - \10\ -

    - The added part is highlighted in yellow below. Ensure that it is inside the \ tag as follows: -
    - \image html transientcache.PNG -

    -4. Edit "C:\Bitnami\solr-4.10.3-0\apache-solr\resources/log4j.properties" to configure Solr log settings: - - Increase the log rotation size threshold (_log4j\.appender\.file\.MaxFileSize_) from 4MB to 100MB. - - Remove the _CONSOLE_ appender from the _log4j\.rootLogger_ line. -

    - The log file should end up looking like this (modified lines are highlighted in yellow -

    - \image html log4j.PNG -

    -5. From an Autopsy installation, copy the folder "C:\Program Files\Autopsy-XXX(current version)\autopsy\solr\solr\configsets" to "C:\Bitnami\solr-4.10.3-0\apache-solr\solr". -6. From an Autopsy installation, copy the folder "C:\Program Files\Autopsy-XXX(current version)\autopsy\solr\solr\lib" to "C:\Bitnami\solr-4.10.3-0\apache-solr\solr". -7. From an Autopsy installation, copy the file "C:\Program Files\Autopsy-XXX(current version)\autopsy\solr\solr\zoo.cfg" to "C:\Bitnami\solr-4.10.3-0\apache-solr\solr". -8. Stop the solrJetty service by pressing Start, typing services.msc, pressing Enter, and locating the solrJetty Windows service. Select the service and press Stop the service. If the service is already stopped and there is no Stop the service available, this is okay. -9. Start a Windows command prompt as administrator by pressing Start, typing command, right clicking on Command Prompt, and clicking on Run as administrator. Then run the following command to uninstall the solrJetty service: - - cmd /c C:\Bitnami\solr-4.10.3-0\apache-solr\scripts\serviceinstall.bat UNINSTALL - - You will very likely see a result that says "The solrJetty service is not started." This is okay. -10. Start a Windows command prompt as administrator by pressing Start, typing command, right clicking on Command Prompt, and clicking on Run as administrator. Then run the following command to install the solrJetty service: - - cmd /c C:\Bitnami\solr-4.10.3-0\apache-solr\scripts\serviceinstall.bat INSTALL -
    Note the argument "INSTALL" is case sensitive. Your command prompt should look like the screenshot below. Very likely your command prompt will say "The solrJetty service could not be started." This is okay. -

    - \image html solrinstall1.PNG -

    - -\subsection running_solr Running Solr - -To run Solr, first you'll need to choose which account should run the service. Once you've decided, you'll start the service through the Windows Service Manager. Both of these steps are described below. - -\subsubsection solr_user Choosing the User Account for the Solr Service - -Solr will run as a Windows service as a specific user. If the wrong user account is used, Solr will not have permissions to write indexes. - -Generally, you'll want to run Solr as a Windows Domain account. You will NOT want to use the default setup, which is to run Solr as a "Local Service Account", because this account does not have access to network-based shared storage. The one exception is if the computer running Solr is also the computer that hosts the shared storage. In that is the case, then the "Local Service Account" will still be able to access the shared storage since the network is not required. - -\subsubsection start_solr Starting Solr -

      -
    1. You should be able to see the Solr service in a web browser via the URL http://localhost:8983/solr/#/ as shown in the screenshot below. If you can, you should skip the next step. If you cannot, proceed to the next step. - -
    2. Press _Start_, type _services.msc_, and press _Enter_. Find _solrJetty_. If the service is running, press _Stop the service_, then double click it, and switch to the _Log On_ tab to change the logon credentials to a user who will have access to read and write the primary shared drive. Refer to the \ref solr_user section to see which account type is appropriate for your case. - -If the machine is on a domain, the Account Name will be in the form of _DOMAINNAME\\username_ as shown in the example below. Note that in the screenshot below, the domain name is _DOMAIN_ and the user name is _username_. These are just examples, not real values. - -\image html solrinstall2.PNG - -If the machine is on a domain, **make sure** to select the domain with the mouse by going to the _Log On_ tab, clicking _Browse_, then clicking _Locations_ and selecting the domain of interest. Then enter the user name desired and press _Check Names_. When that completes, press _OK_, type in the password once for each box and press _OK_. You may see "The user has been granted the log on as a service right." - -
    3. You should be able to see the Solr service in a web browser via the URL http://localhost:8983/solr/#/ as shown in the screenshot below. -

      - \image html solrinstall3.PNG -

      -If the service is appropriately started and you are unable to see the screenshot above, contact your network administrator to open ports in the firewall. -

      -Warning: The Solr process must have adequate permissions to write data to the main shared storage drive where case output will be stored. -
    -*/ diff --git a/docs/doxygen-user/installation.dox b/docs/doxygen-user/installation.dox index aa67e55d7a..57e4f59a2f 100644 --- a/docs/doxygen-user/installation.dox +++ b/docs/doxygen-user/installation.dox @@ -2,30 +2,44 @@ [TOC] -\section prereqs Prerequisites -It is _highly_ recommended to remove or disable any antivirus software from computers that will be processing or reviewing cases. Antivirus software will often conflict with forensic software, and may quarantine or even delete some of your results before you get a chance to look at them. -

    \section install Deployment Types -Starting with Autopsy 4.0, there are two ways to deploy Autopsy: -- **Single-User**: Cases can be open by only a single instance of Autopsy at a time. Autopsy installations do not communicate with each other. This is the easiest to install and deploy. This page outlines that installation process. -- **Multi-User**: Cases can be open by multiple users at the same time and users can see what each other is doing. This collaborative deployment requires installation and configuration of other network-based services. The installation of this deployment is covered in \ref install_multiuser_page. -\section sysreqs System Memory Requirements -The 64 bit version of Autopsy requires a minimum of 8GB RAM (16 GB recommended). -When the 64 bit version of Autopsy is installed on Windows it will be limited to a maximum heap size of 4GB leaving the remaining memory for the operating system, the internal Solr text indexing service and other applications. If you wish to change the maximum heap size you can do so after installation by changing the Maximum JVM Memory value in the Runtime section under Tools -> Options -> Application. +There are two ways to deploy Autopsy: +- **Single-User**: Cases can be opened by only a single instance of Autopsy at a time. Autopsy installations do not communicate with each other. This is the easiest to install and deploy. This page outlines that installation process. +- **Multi-User**: Cases can be opened by multiple users at the same time and users can see what each other is doing. This collaborative deployment requires installation and configuration of other network-based services. The installation of this deployment is covered in \ref install_multiuser_page. -\image html runtime_settings.PNG \section download Download -Download Autopsy from the website: +Regardless of the deployment type, you can download Autopsy from the website: http://sleuthkit.org/autopsy/download.php -The current version of Autopsy 4 is distributed on sleuthkit.org only as a Windows installer. It can run on Linux and OS X, but requires some manual setup. +We distribute a Windows installer and ZIP files to run on Linux and OS X. -\section install_standalone Installation + +\section install_reqs System Requirements + +\subsection prereqs_av AntiVirus + +You should consider disabling AntiVirus software that runs on your computers that will be running Autopsy. AntiVirus software may quarantine or even delete some of your results before you get a chance to look at them. Autopsy encodes some of the files that it extracts so that they can not be executed or scanned, but some modules (such as our ZIP extraction module) will directly write files to disk in an unencoded format. + +Of course, the risk with disabling AntiVirus software is that your computer could be infected with malware that came from your media. + + + +\subsection sysreqs Memory + +We recommend a minimum of 16GB of RAM. + +By default, Autopsy will use a maximum of 4GB of RAM (not including memory that the Solr text indexing server uses). You can increase this size after installation by changing the Maximum JVM Memory value in the Runtime section under Tools -> Options -> Application. + +\image html runtime_settings.PNG + + + +\section install_standalone Single-User Installation To install Autopsy, perform the following steps: 1. Run the Autopsy _msi_ file 2. If Windows prompts with User Account Control, click _Yes_ @@ -33,4 +47,12 @@ To install Autopsy, perform the following steps: 4. Autopsy should now be fully installed +\section install_proxy Proxies + +If you are behind a proxy and need access to a network with Autopsy or one of the modules, you may set your proxy information in the _Tools_, _Options_, _General_ tab as shown in the screenshot below. + +

    +\image html proxySettings.PNG +

    + */ diff --git a/docs/doxygen-user/main.dox b/docs/doxygen-user/main.dox index 5d5e3063cd..7087c42ec2 100644 --- a/docs/doxygen-user/main.dox +++ b/docs/doxygen-user/main.dox @@ -17,7 +17,7 @@ The following topics are available here: - \subpage cases_page - \subpage ds_page - \subpage uilayout_page -- Automated Analysis (Modules) +- Ingest Modules - \subpage ingest_page "Ingest Modules" - \subpage recent_activity_page - \subpage hash_db_page @@ -34,7 +34,7 @@ The following topics are available here: - \subpage cr_ingest_module - \subpage encryption_page - \subpage vm_extractor_page -- Manual Analysis +- Reviewing the Results - \subpage tree_viewer_page - \subpage result_viewer_page - \subpage content_viewer_page @@ -55,19 +55,18 @@ The following topics are available here: - \subpage reporting_page - \subpage module_install_page - \subpage performance_page -- Multi-user Collaborative Deployments +- Multi-user Cluster - \subpage install_multiuser_page - - \subpage install_activemq - - \subpage install_postgresql - - \subpage install_solr - - \subpage windows_authentication - \subpage multiuser_sec_page - \subpage multiuser_page - Triage - \subpage triage_page - \subpage live_triage_page -- \subpage advanced_page -- \subpage experimental_page +- Experimental Module + - \subpage experimental_page + - \ref auto_ingest_page + - \ref object_detection_page + - \ref volatility_dsp_page - \subpage command_line_ingest_page - \subpage logical_imager_page - \subpage translations_page diff --git a/docs/doxygen-user/multiuser.dox b/docs/doxygen-user/multi-user/createMultiUserCase.dox similarity index 98% rename from docs/doxygen-user/multiuser.dox rename to docs/doxygen-user/multi-user/createMultiUserCase.dox index 99141d0a22..e0e46d156e 100644 --- a/docs/doxygen-user/multiuser.dox +++ b/docs/doxygen-user/multi-user/createMultiUserCase.dox @@ -7,7 +7,7 @@ Multi-user cases allow multiple instances of Autopsy to have the same case open \image html case-newcase.PNG To create a multi-user case, the following must occur: -- The network services must be installed, configured, and running. See \ref multiuser_install_services. +- The network services must be installed, configured, and running. See \ref multiuser_install_install. - The Case folder needs to be in a shared folder that all other clients can also access at the same path (UNC or drive letter). - The data sources that are added with the Add Data Source wizard must be in a shared folder that all clients can access at the same path. diff --git a/docs/doxygen-user/installActiveMQ.dox b/docs/doxygen-user/multi-user/installActiveMQ.dox similarity index 82% rename from docs/doxygen-user/installActiveMQ.dox rename to docs/doxygen-user/multi-user/installActiveMQ.dox index fec69c00de..fd0726e1b1 100644 --- a/docs/doxygen-user/installActiveMQ.dox +++ b/docs/doxygen-user/multi-user/installActiveMQ.dox @@ -1,5 +1,9 @@ /*! \page install_activemq Install and Configure ActiveMQ -To install ActiveMQ, perform the following steps: + +\section install_activemq_overview Overview + +ActiveMQ is a messaging service that allows the Autopsy clients to communicate with each other. This allows each client to get real-time updates. This service has minimal storage requirements. + \section install_activemq_prereq Prerequisites @@ -32,15 +36,27 @@ If you need the JRE, install it with the default settings. 4. Start the ActiveMQ service by pressing _Start_, type _services.msc_, and press _Enter_. Find _ActiveMQ_ in the list and press the _Start the service_ link. -5. ActiveMQ should now be installed and configured using the default credentials. You should go to the next section to change the default passwords. To test your installation, you can access the admin pages in your web browser via a URL like this (set your host): http://localhost:8161/admin. The default administrator username is _admin_ with a password of _admin_ and the default regular username is _user_ with a default password of _password_. You can change these passwords by following the instructions below. If you can see a page that looks like the following, it is ready to function. +5. ActiveMQ should now be installed and configured using the default credentials. + + +\subsection install_activemq_test Testing + +To test your installation, you can access the admin pages in your web browser via a URL like this: http://localhost:8161/admin. + +The default administrator username is _admin_ with a password of _admin_ and the default regular username is _user_ with a default password of _password_. You can change these passwords by following the instructions below. + +If you can see a page that looks like the following, the service is ready to function.

    \image html activemq.PNG

    -If you do not see a screen like the above screenshot and you have double checked that the ActiveMQ service is running, contact your network administrator. For the ActiveMQ service to be accessible by network clients you may need to configure your Windows firewall (and any other 3rd party firewall in use) to allow communication. +If you do not see this page: +- Double check that the ActiveMQ service is running +- Check that the port is not being blocked by a firewall. -\subsection install_activemq_install_pw Configuring Authentication + +\section install_activemq_install_pw Configuring Authentication You can optionally add authentication to your ActiveMQ server. The ActiveMQ communications are not encrypted and contain basic messages between the systems about when new data has been found. @@ -60,6 +76,7 @@ When complete, the file should look like this:

    \image html groups.properties.after.PNG

    + 2. Copy and paste the following text to the file "conf\users.properties", overwriting the text highlighted in yellow in the screenshot below:
    system=manager
    @@ -74,6 +91,7 @@ When complete, the file should look like this:

    \image html users.properties.after.PNG

    + 3. Copy and paste the following text to the file "conf\activemq.xml", inserting the text at the line shown in yellow in the screenshot below. @@ -111,4 +129,8 @@ To add a new user or change the password: \image html StartActiveMQService.PNG

    +\section install_mq_backup Backing Up + +There is nothing to backup for ActiveMQ. It does not store any case-related data in files. + */ diff --git a/docs/doxygen-user/multi-user/installMultiUser.dox b/docs/doxygen-user/multi-user/installMultiUser.dox new file mode 100644 index 0000000000..7a67bd6ade --- /dev/null +++ b/docs/doxygen-user/multi-user/installMultiUser.dox @@ -0,0 +1,40 @@ +/*! \page install_multiuser_page Setting Up Multi-user Cluster + +\section multiuser_install_overview Overview + +Autopsy can be setup to work in an environment where multiple users on different computers can have the same case open at the same time. To set up this type of environment, you will need to configure additional (free and open source) network-based services. + +The basic concept is that you'll have a central: +- Database +- Keyword search index +- Storage + +Each Autopsy client will then use those shared resources instead of the embedded versions that are used for single-user cases. + +\image html multi-user-network.png + + +\section multiuser_install_install Cluster Installation and Configuration + +Let's now step through the process of setting up an Autopsy cluster. + +Step 1: \ref multiuser_system + +Step 2: \ref multiuser_users + +Step 3: \ref multiuser_storage + +Step 4: \ref install_postgresql + +Step 5: \ref install_solr + +Step 6: \ref install_activemq + +Step 7: \ref multiuser_install_clients + + + +When you setup the above services, write down the addresses, user names, and passwords or each so that you can configure each of the client systems afterwards. + + +*/ diff --git a/docs/doxygen-user/multi-user/installMultiUserClient.dox b/docs/doxygen-user/multi-user/installMultiUserClient.dox new file mode 100644 index 0000000000..d0f3fdeb6a --- /dev/null +++ b/docs/doxygen-user/multi-user/installMultiUserClient.dox @@ -0,0 +1,22 @@ +/*! \page install_multiuserclient_page Setting Up Multi-user Client + + +\section multiuser_install_clients Install Autopsy Clients + +Once the infrastructure is in place, you can configure Autopsy clients to use them. +- Install Autopsy on each client system. Use the normal installer and pick the defaults. +- Test that the user has access to the shared storage by opening the shared storage folders using Windows Explorer. If a password prompt is given, then enter the password and store the credentials (see \ref multiuser_users_store). +- Start Autopsy and open the multi-user settings panel from "Tools", "Options", "Multi-user". As shown in the screenshot below, you can then enter all of the address and authentication information for the network-based services. Note that in order to create or open Multi-user cases, "Enable Multi-user cases" must be checked and the settings below must be correct. + +\image html multiuser_settings.PNG + +- For each setting, press the "Test" button to ensure that Autopsy can communicate with each service. If any fail, then refer to the specific setup page for testing options. Also check that a firewall is not blocking the communications. + + - NOTE: None of these tests are for permissions on the shared storage because Autopsy does not know about the shared storage. It can't test that until you make a case. + +- Make a test case (see \ref creating_multi_user_cases). You can add a single file in as a logical data source. The key concept is to look for errors. + - If you find errors, look for errors in the log file on the Autopsy client. + - If you followed all of the previous steps in all of the previous pages, then a common error at this point is that Solr cannot access the shared storage and it is running as a Service account. When this happens, you'll see an error message about Solr not being able to create or access a "core". If this happens, review what user Solr should be running as (see \ref multiuser_users_solr) and change the shared storage configuration or ensure that credentials are stored. + + +*/ \ No newline at end of file diff --git a/docs/doxygen-user/installPostgres.dox b/docs/doxygen-user/multi-user/installPostgres.dox similarity index 50% rename from docs/doxygen-user/installPostgres.dox rename to docs/doxygen-user/multi-user/installPostgres.dox index 07650aee24..1f089c781b 100644 --- a/docs/doxygen-user/installPostgres.dox +++ b/docs/doxygen-user/multi-user/installPostgres.dox @@ -1,55 +1,52 @@ /*! \page install_postgresql Install and Configure PostgreSQL + + +\section install_post_over Overview + +In a multi-user case, a central PostgreSQL database server is used instead of the embedded SQLite databases. + +A new database will be created for each case and the database will be stored on the local drive. + +You should ensure that the database folder is backed up. + +\section install_post_install Installation + To install PostgreSQL, perform the following steps: -1. Download a 64-bit PostgreSQL installer from http://www.enterprisedb.com/products-services-training/pgdownload#windows Choose the one that says _Win X86-64_. Autopsy has been tested with PostgreSQL version 9.5. +1. Download a 64-bit PostgreSQL installer from http://www.enterprisedb.com/products-services-training/pgdownload#windows Choose the one that says _Win X86-64_. Autopsy has been tested with PostgreSQL version 9.5. -2. Run the installer. The name will be similar to _postgresql-9.5.3-1-windows-x64.exe_. +2. Run the installer. The name will be similar to _postgresql-9.5.3-1-windows-x64.exe_. -3. You may accept defaults for all items except for the password as you work through the wizard. Do not lose the password you enter in. This is the PostgreSQL administrator login password. +3. You may accept defaults for all items except for the password as you work through the wizard. Do not lose the password you enter in. This is the PostgreSQL administrator login password. -4. You do not need to launch the StackBuilder nor acquire any more software from it. Uncheck the option to use StackBuilder and press _Finish_. +4. You do not need to launch the StackBuilder nor acquire any more software from it. Uncheck the option to use StackBuilder and press _Finish_. -5. Create a regular user account to use while running Autopsy. You can do this with either of two methods, graphically, or command line. We cover graphically first. - - Graphically: - - Using the PostgreSQL administrator login and the pgAdmin III tool, create a regular user account to use while running Autopsy. - - Right click on "Login Roles" and select "New Login Role..." as shown below: +\section install_post_config Configuration + +1. Create a regular database user account that Autopsy will use. You can do this with either of two methods, graphically, or command line. We cover graphically here. + + +- Use the pgAdmin III tool and login with the PostgreSQL administrator login. +- Right click on "Login Roles" and select "New Login Role..." as shown below:

    \image html pgAdmin.PNG

    - - Enter the user name you would like to use in the "Role name" field. +- Enter the user name you would like to use in the "Role name" field.

    \image html newLoginRole.PNG

    - - Enter the password on the "Definition" tab. +- Enter the password on the "Definition" tab.

    \image html newPassword.PNG

    - - Check "Can create databases" on the "Role Privileges" tab. +- Check "Can create databases" on the "Role Privileges" tab.

    \image html newRights.PNG

    - - Click "OK". -

    - - Command line: -
    - Use the _psql_ tool. To start _psql_, press _Start_, type _psql_, and press _Enter_ a few times until it prompts you for a password. Type in the password you gave it when installing PostgreSQL. You should see a prompt that looks like the screenshot below. -

    -\image html postgresqlinstall1.PNG -

    -If you want your user account name to be "Autopsy" and your password to be "myPassword", use the following command to create a new user, noting that the password is enclosed in single quotes, __not backticks nor double quotes__. Also note that it is important to type this command in from the keyboard directly, as copying and pasting can sometimes yield different characters for single quotes that can confuse _psql_. -

    -The command is: -
    -> CREATE    USER    Autopsy    WITH    PASSWORD    'myPassword'    CREATEDB; -
    -When you see the _CREATE ROLE_ output as shown in the screenshot below, the new user has been created. You can close the _psql_ window now. -
    -
    -\image html postgresqlinstall2.PNG -
    +- Click "OK". -6. Edit C:\\Program Files\\PostgreSQL\\9.5\\data\\pg_hba.conf to add an entry to allow external computers to connect via the network. +2. Edit C:\\Program Files\\PostgreSQL\\9.5\\data\\pg_hba.conf to add an entry to allow external computers to connect via the network.

    First, find your machine's IPv4 address and Subnet Mask (Press _Start_, type _cmd_, type _ipconfig_ and parse the results. The IP address is shown in yellow below.
    @@ -72,7 +69,7 @@ Add the line highlighted in yellow below, formatted with spaces between the entr If you intend to use PostgreSQL from machines on a different subnet, you need an entry in the _pg_hba.conf_ file for each subnet.

    -7. Uncomment the following entires in the configuration file located at C:\\Program Files\\PostgreSQL\\9.5\\data\\postgresql.conf by removing the leading "#", and change their values "off" as shown below. +3. Uncomment the following entires in the configuration file located at C:\\Program Files\\PostgreSQL\\9.5\\data\\postgresql.conf by removing the leading "#", and change their values "off" as shown below.
    > fsync = off
    > synchronous_commit = off
    @@ -89,16 +86,34 @@ To this: Note the removal of the leading number symbol-this uncomments that entry.

    -8. Still in "C:\Program Files\PostgreSQL\9.5\data\postgresql.conf", find the entry named _max_connections_ and set it to the number of suggested connections for your configuration. A rule of thumb is add 100 connections for each Automated Ingest Node and 100 connections for each Reviewer node you plan to have in the network. More information is available at 5.1.1. See the screenshot below. +4. Still in "C:\Program Files\PostgreSQL\9.5\data\postgresql.conf", find the entry named _max_connections_ and set it to the number of suggested connections for your configuration. A rule of thumb is add 100 connections for each Automated Ingest Node and 100 connections for each Reviewer node you plan to have in the network. See the screenshot below.

    \image html maxConnections.PNG

    -9. Press _Start_, type _services.msc_, and press _Enter_. Select _postgresql-x64-9.5_ in the services list and click the link that says _Stop the service_ then click the link that says _Start the service_ as shown in the screenshot below. + + +5. Restart the service via the Services panel by pressing _Start_, type _services.msc_, and press _Enter_. Select _postgresql-x64-9.5_ in the services list and click the link that says _Stop the service_. If you want PostgreSQL to run as a different user (you don't need to), then make that change now. When done, click the link that says _Start the service_ as shown in the screenshot below.

    \image html postgresqlinstall7.PNG

    -PostgreSQL should now be up and running. You can verify by using either the _pgAdmin_ tool or the _psql_ tool to connect to the database server from another machine on the network . -


    + + +\section install_post_test Testing + +You can verify that PostgreSQL is running by using either the _pgAdmin_ tool or the _psql_ tool to connect to the database server from another machine on the network. + +Common problems are from: +- Firewall blocking the ports on the PostgreSQL server +- Incorrect credentials +- Wrong IP address range in the configuration file + + +\section install_post_backup Backing Up + +The database are stored on the local drive (not shared storage). So, you should backup that directory periodically. + +The default location is XYZ. + */ diff --git a/docs/doxygen-user/multi-user/installSharedStorage.dox b/docs/doxygen-user/multi-user/installSharedStorage.dox new file mode 100644 index 0000000000..3152ed3ab0 --- /dev/null +++ b/docs/doxygen-user/multi-user/installSharedStorage.dox @@ -0,0 +1,42 @@ +/*! \page install_multiuser_storage_page Setting Up Shared Storage + +\section multiuser_storage Set Up Shared Storage + +The cluster will need shared storage that can be accessed from: +- Autopsy clients +- Solr server + +This shared storage will be used for both data sources and case outputs, so you will need lots of space. + +Specific configuration of the shared storage will depend on what type of file share you have. Examples include Windows file sharing, Linux Samba, or a NAS using FibreChannel. + +Below are some general guidelines to use during the setup and configuration. + +\subsection multiuser_storage_req Requirements +- All computers will need to access the shared storage at the exact same path. So, all must have the same drive letters or be able to resolve the same host names (for UNC paths). +- If Solr is running as a Windows service, you will likely need to use UNC paths. The service will not have access to drive letters for network mounted drives (such as Windows file sharing). If you have a hardware-based NAS, then you may be able to use drive letters. +- The user accounts that Autopsy and Solr are running as will need permissions to read and write to the shared storage. See \ref multiuser_users for tips on picking user accounts and saving credentials. + + +\subsection multiuser_storage_con Considerations +- You will probably get better performance by having different drives for inputs (disk images) and outputs (case folders). +- If you separate the shares, the "inputs" share can be provided as read-only if you do not want clients to modify the data sources. + + +\subsection multiuser_storage_ex Example +- Windows Server +- Dedicated SSD drives for inputs (data sources) and outputs (case folder). +- Each drive is shared with names "DataSources" and "Cases". +- If the server is not part of a domain, local accounts are created on it for each user that will be running Autopsy or Solr. Each account will have the same password on all systems. +- If Solr will be running as the NetworkService account, grant access to the shares for the computer running Solr (i.e. not just a specific user). + + +\subsection multiuser_storage_test Testing +- Before you proceed to setup any other services, you should test that the computers can access the share. Permission problems with the shares are the most common configuration challenge. +- Log into a computer that will eventually be an Autopsy client using an account that Autopsy will run as. +- Access the share, such as \\\\autopsy_storage\\Cases. +- If you get prompted for a password, then either: + - Store the credentials, as outlined in \ref multiuser_users_store. You'll need to repeat this on all of the clients and Solr server. + - Reconfigure the shared storage server if the prompt was because of an error. This may include ensuring that they both have the same password. + +*/ diff --git a/docs/doxygen-user/multi-user/installSolr.dox b/docs/doxygen-user/multi-user/installSolr.dox new file mode 100644 index 0000000000..6070fc65e1 --- /dev/null +++ b/docs/doxygen-user/multi-user/installSolr.dox @@ -0,0 +1,145 @@ +/*! \page install_solr Install and Configure Solr + +\section install_solr_overview Overview + +Autopsy uses Apache Solr to store keyword text indexes. A central server is needed in a multi-user cluster to maintain and search the indexes. + +A new Solr core is created for each case and the core/index is stored in the case folder (not on the local drive of the Solr server). + +Solr's embedded Zookeeper is also used as a coordination service for Autopsy. + +\section install_solr_prereq Prerequisites + +We use Bitnami Solr, which packages Solr as a Windows service. + +You will need: +- A 64-bit version of the Java Runtime Environment (JRE) from http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html. +- The Apache Solr 4.10.3-0 installation package. This is no longer available from its original source, but you can find it on our site: https://sourceforge.net/projects/autopsy/files/CollaborativeServices/Solr. +- An installed version of Autopsy so that you can copy files from it. You can install Autopsy on one of the planned client systems. You do not need to install it on the Solr server. + + + + + +- A network-accessible machine to install Solr on. Note that the Solr process will need to write data out to the main shared storage drive, and needs adequate permissions to write to this location, which may be across a network. + + + +\section install_solr_install Installation + +\subsection install_solr_install_java JRE Installation +1. JREs are normally installed under "C:\Program Files\Java\jre(version)", so check there to see if you have one installed already. If not, get the installer as listed in the above Prerequisites section and install it with the default settings. + +\subsection install_solr_install_solr Solr Installation + +The following steps will configure Solr to run using an account that will have access to the network storage. + +1. Run the Bitnami installer, "bitnami-solr-4.10.3-0-windows-installer.exe" +2. If Windows prompts with User Account Control, click _Yes_ +3. Follow the prompts through to completion. You do not need to "Learn more about Bitnami cloud hosting" so you can clear the check box. +4. If you see an error dialog like the following, you may safely ignore it. +

    +\image html apachebadmessage.PNG +
    +5. When the installation completes, clear the "Launch Bitnami Apache Solr Stack Now?" checkbox and click _Finish_. + + + +\subsection install_solr_config Solr Configuration + +1. Stop the _solrJetty_ service by pressing _Start_, typing _services.msc_, pressing _Enter_, and locating the _solrJetty_ Windows service. Select the service and press _Stop the service_. If the service is already stopped and there is no _Stop the service_ available, this is okay. +2. Service Configuration: Edit the "C:\Bitnami\solr-4.10.3-0\apache-solr\scripts\serviceinstall.bat" script. You need administrator rights to change this file. The easiest way around this is to save a copy on the Desktop, edit the Desktop version, and copy the new one back over the top of the old. Windows will ask for permission to overwrite the old file; allow it. You should make the following changes to this file: +
    +
    + - Add the following options in the line that begins with "C:\Bitnami\solr-4.10.3-0/apache-solr\scripts\prunsrv.exe" : + + ++JvmOptions=-Dcollection.configName=AutopsyConfig + + ++JvmOptions=-Dbootstrap_confdir="C:\Bitnami\solr-4.10.3-0\apache-solr\solr\configsets\AutopsyConfig\conf" + + ++JvmOptions=-DzkRun +
    + - Replace the path to JavaHome with the path to your 64-bit version of the JRE. If you do not know the path, the correct JavaHome path can be obtained by running the command "where java" from the Windows command line. An example is shown below. The text in yellow is what we are interested in. Do not include the "bin" folder in the path you place into the JavaHome variable. A correct example of the final result will look something like this: –-JavaHome="C:\Program Files\Java\jre1.8.0_111" +

    + A portion of an updated _serviceinstall.bat_ is shown below, with the changes marked in yellow. +

    + \image html serviceinstall.PNG +

    +3. Solr Configuration: Edit "C:\Bitnami\solr-4.10.3-0\apache-solr\solr\solr.xml" to set the _transientCacheSize_ to the maximum number of cases expected to be open concurrently. If you expect ten concurrent cases, the text to add is + \10\ +

    + The added part is highlighted in yellow below. Ensure that it is inside the \ tag as follows: +
    + \image html transientcache.PNG +

    +4. Log Configuration: Edit "C:\Bitnami\solr-4.10.3-0\apache-solr\resources/log4j.properties" to configure Solr log settings: + - Increase the log rotation size threshold (_log4j\.appender\.file\.MaxFileSize_) from 4MB to 100MB. + - Remove the _CONSOLE_ appender from the _log4j\.rootLogger_ line. +

    + The log file should end up looking like this (modified lines are highlighted in yellow +

    + \image html log4j.PNG +

    +5. Schema Configuration: From an Autopsy installation, copy the following into "C:\Bitnami\solr-4.10.3-0\apache-solr\solr": +- The folder "C:\Program Files\Autopsy-XXX(current version)\autopsy\solr\solr\configsets" +- The folder "C:\Program Files\Autopsy-XXX(current version)\autopsy\solr\solr\lib" +- The file "C:\Program Files\Autopsy-XXX(current version)\autopsy\solr\solr\zoo.cfg" + + +\subsection install_solr_reinstall Reinstall Service + +Because we made changes to the service configuration, we need to reinstall it. + +1. Start a Windows command prompt as administrator by pressing Start, typing command, right clicking on Command Prompt, and clicking on Run as administrator. Then run the following command to uninstall the solrJetty service: + + cmd /c C:\Bitnami\solr-4.10.3-0\apache-solr\scripts\serviceinstall.bat UNINSTALL + + You will very likely see a result that says "The solrJetty service is not started." This is okay. +2. In the same prompt, run the following command to install the solrJetty service: + + cmd /c C:\Bitnami\solr-4.10.3-0\apache-solr\scripts\serviceinstall.bat INSTALL +
    Note the argument "INSTALL" is case sensitive. Your command prompt should look like the screenshot below. Very likely your command prompt will say "The solrJetty service could not be started." This is okay. +

    + \image html solrinstall1.PNG +

    + + +You should be able to see the Solr service in a web browser via the URL http://localhost:8983/solr/#/ as shown in the screenshot below. If you can, you should skip the next step. If you cannot, proceed to the next step. + + +\subsection install_solr_service_user Configure Service User + +Back in \ref install_multiuseruser_page, you should have decided what user to run Solr as. To configure Solr to run as that user, you'll use Windows Service Manager. + + +- Press _Start_, type _services.msc_, and press _Enter_. +- Find _solrJetty_. If the service is running, press _Stop the service_, +- Double click the service and switch to the _Log On_ tab to change the logon credentials to the chosen user who will have access to the shared storage. + - If you specify a domain account, the account name will be in the form of _DOMAINNAME\\username_ as shown in the example below + +\image html solrinstall2.PNG + + +- Start the service again. + +\section install_solr_test Testing + +There are two tests that you should do before declaring that Solr has been configured. + +- Web Interface: You should be able to see the Solr admin panel in a web browser via the URL http://localhost:8983/solr/#/. +

    + \image html solrinstall3.PNG +

    + +If the service is appropriately started and you are unable to see the screenshot above, then it could be that port 8983 is blocked by your firewall. Contact your network administrator to open this port. + +- Shared Storage: If you are running Solr as a normal user, then log into the Solr computer as that user and and access the shared storage paths. Ensure that you can access the UNC paths (or drive letters if you have hardware NAS). If you are prompted for a password to access the shared storage, then either enter the password and choose to save the credentials or reconfigure the setup so that the same passwords are used, etc. See \ref multiuser_users_store for steps on storing credentials. If you needed to store the credentials, then you should restart the service or reboot the computer (we have observed that a running service does not get the updated credentials). + +NOTE: You can not do a full test of permissions until you make a test case after all of the services are configured. + +\section install_solr_backup Backing Up + +Solr creates two types of data that need to be backed up: +- Text Indexes: These are stored in the case folder on the shared storage and should get backed up. +- ZooKeeper Data: Autopsy uses a service called ZooKeeper that Solr runs and it stores data about what cases exist and who has them open. You should back this data up so that you can have a list of all available multi-user cases. + - That data is stored in X. + + +*/ diff --git a/docs/doxygen-user/multi-user/installSystems.dox b/docs/doxygen-user/multi-user/installSystems.dox new file mode 100644 index 0000000000..400ecd6dd3 --- /dev/null +++ b/docs/doxygen-user/multi-user/installSystems.dox @@ -0,0 +1,42 @@ +/*! \page install_multiuser_systems_page Multi-user Hardware / VM Selection + +\section multiuser_system Pick Your Hardware / VM Configuration + +The first step in setting up a multi-user cluster is picking how many computers or VMs you'll use to run the various services. + +From a service perspective, you'll need to run: +- PostgeSQL database server +- Apache Solr text indexing server +- ActiveMQ messaging server +- Network storage + +You can run each of these on their own dedicated VM, but that is not necessary. We recommend: + +- Server 1: PostgreSQL and ActiveMQ + - Both of these are low overhead services. +- Server 2: Apache Solr and Shared Storage (if you are using Windows File Sharing). + - Apache Solr uses a lot of memory, so we keep it by itself. + - The benefit of running the shared storage on the same computer as Solr is performance. Solr is much faster when it can write to local storage instead of over the network. + - If you are using a different form of shared storage, then we still recommend keeping Solr on its own. + + +\subsection multiuser_system_hw Suggested Hardware + +TODO + +- PostgreSQL/ActiveMQ (server 1): + - RAM: + - Local Storage: Enough for databases +- Solr (server 2): + - RAM: + - Local Storage: Minimal + +\subsection multiuser_system_back Backups + +You will have lots of important data on the system. Ensure that the following are regularly backed up: +- Shared storage (which contains case data and text indexes) +- Databases on the PostgreSQL server (see \ref install_post_backup) +- Zookeeper data on the Solr server (see \ref install_solr_backup) + + +*/ \ No newline at end of file diff --git a/docs/doxygen-user/multi-user/installUsers.dox b/docs/doxygen-user/multi-user/installUsers.dox new file mode 100644 index 0000000000..1b29230258 --- /dev/null +++ b/docs/doxygen-user/multi-user/installUsers.dox @@ -0,0 +1,75 @@ +/*! \page install_multiuseruser_page Picking Users + +\section multiuser_users Pick Your User Accounts + +Before you get into configuring any computers, you should have an understanding about what user accounts will be used. User account permissions are one of the most common challenges people have when setting up a cluster. + +There are two major decisions to make about users: +- The user for Autopsy. +- The user for the Solr service. + +These users are important because they will need access to the shared storage without needing to be prompted for a password. Other services, such as PostgreSQL and ActiveMQ, can run as the default service account because they use only local storage. + +The choice you make here will depend on what type of shared storage platform you are using and what kind of Windows-based infrastructure you have. + + +\subsection multiuser_users_autopsy Autopsy User + +The user account that Autopsy runs as will need access to the shared storage. There are three general options: + +- Domain Accounts: If the cluster is on a Windows domain, then Autopsy can be run with a domain account. + - If your shared storage is a Windows file share, then users should be able to access it without needing a password. + - If your shared storage is another platform, then you'll likely need to force Windows to store the shared storage credentials (as described below). +- Unique Local Unique Accounts: Some clusters are not on a Windows domain and have unique accounts for each analyst / user. + - If your shared storage is a Windows file share, then users will not need to enter their password IF the same user name and password exists on the file share server. + - Otherwise, you'll need to force Windows to store the credentials. +- Shared Local Account: Lastly, some clusters use a single local account, such as one named "autopsy" for all users. This is not recommended because Autopsy uses the login name to track who did certain actions, such as tagging files. + - The same password rules apply here as with the previous scenario. Either have the same password on all systems or force Windows to store the passwords. + + +\subsection multiuser_users_solr Solr Service + +Solr will run as a Windows service and will need access to the shared storage. The default user, which is "LocalService", will not have access to network-based storage. + +So, if you have network-based shared storage, you have three options: +- NetworkService: If you are on a domain, you maybe able to run Solr as the "NetworkService" account. This account has access to the network, but the challenge can be granting access for this account to the shared storage. + - If your shared storage is a Windows file share, you'll need to grant access to the computer running Solr. + - For other shared storage, you may not be able to access the data from the NetworkService account. +- Normal User: If you are not on a domain or cannot grant access to the computer for share storage, then run Solr as a normal user (local or domain). + - If you do this, refer to the scenarios as outlined above for picking an Autopsy user. The same rules will apply with respect to passwords and saving credentials. + - The main downside of this is that the service needs to be updated when the account password changes and it may require letting others know about the password. +- LocalService: Lastly, if you using the same server for both Solr and shared storage, then it is possible to run Solr as the default "LocalService" because it doesn't need network access. + + + + +\section multiuser_users_store Storing Credentials + +Based on your shared storage and your above choice for user accounts, you may need to force each Windows computer to store credentials for the shared storage. For example, if your shared storage is a Linux-based system. + + +To store the credentials on a given computer, we simply access the shared storage. Windows will prompt us for a password and we choose the option to save the credentials. We will repeat this on each computer for each user account and using both the hostname and IP address of the storage. If two examiners will be using the same Autopsy client computer and they have their own accounts, you'll need to do this for both users. + +- Launch Windows Explorer and type the UNC path of the shared storage using the IP address, such as "\\10.10.152.211\Cases". Press Enter. +

    +\image html urlInAddressbar.PNG +

    + +- If the folder opens up without prompting for a password, then you are OK. If your credentials are needed, you will see a dialog similar to the following: + +

    +\image html credentialsWithDomain.PNG +

    + +- If your account is part of a Windows domain, add the domain in the top box before the "\". Follow the slash with your username. If you have no domain name, just use your username with no slashes. Add your password in the next box down and place a check mark in "Remember my credentials", then click "OK". + + +Next, repeat with the hostname of the shared storage. For example "\\autopsy_storage\Cases". Again enter your credentials and choose "Remember my credentials". + + +Do these steps for each machine that will be accessing the shared drive. + + +Also note that you will need to repeat this process when the password for the shared storage changes. + +*/ diff --git a/docs/doxygen-user/multiuser-security.dox b/docs/doxygen-user/multi-user/multiuser-security.dox similarity index 100% rename from docs/doxygen-user/multiuser-security.dox rename to docs/doxygen-user/multi-user/multiuser-security.dox diff --git a/docs/doxygen-user/windows_authentication.dox b/docs/doxygen-user/windows_authentication.dox deleted file mode 100644 index d86d0249cd..0000000000 --- a/docs/doxygen-user/windows_authentication.dox +++ /dev/null @@ -1,57 +0,0 @@ -/*! \page windows_authentication Shared Drive Authentication - -

    -If your shared drive is a Windows-hosted shared drive, you will likely need to provide authentication for each machine that connects to the shared drive. This guide only covers Windows-hosted shared drives. - -To authenticate with Windows and allow access to a shared drive, you will need: -- A username -- A password -- The domain name (if the machine hosting the shared drive is on a domain) -- The IP address of the machine hosting the shared drive -- The hostname of the machine hosting the shared drive - -Using Windows Explorer, in the address bar enter two slashes "\\" followed by the storage machine's IP address and press _Enter_. An example is shown below with the text "\\10.10.152.211" entered. -

    -\image html urlInAddressbar.PNG -

    - -You will see a dialog similar to the following, asking for your credentials. - -

    -\image html credentialsWithDomain.PNG -

    - -If you have a domain name, add it in the top box before the "\". Follow the slash with your username. If you have no domain name, just use your username with no slashes. Add your password in the next box down and place a check mark in "Remember my credentials", then click "OK". - -Next, we will do the same steps over again, using the hostname of the machine. This is necessary to authenticate with both IP address access and hostname access. If you do not know the hostname, you may find it by pinging the IP address with the "-a" flag set. It will look something like the screenshot below, where we find the hostname associated with the IP address 10.10.142.56   is   win-kmort-4863.basistech.net. - -

    -\image html getHostname.PNG -

    - -In Windows Explorer, use this hostname preceded by two slashes, "\\", in the address bar as shown below and press enter. - -

    -\image html hostname.PNG -

    - -You will see a screen similar to the screenshot below. Do the same steps with domain, username, and password as you did above. - -

    -\image html toConnect.PNG -

    - -Do these steps for each machine that will be accessing the shared drive. -


    -


    -- - - - - -Note that if you are familiar with the Windows Credential Manager, you may use this tool to manage credentials. These credentials can also be managed from the command line using the "net use" command. To get to Credential Manager click on to Start, and typing "Credential Manager" and pressing enter. A screenshot of the Windows Credential Manager with some domain names intentionally blanked out is shown below. - -

    -\image html credentialManager.PNG -

    - -Also note that authentication and access can be an issue when passwords change. When passwords change, for every computer using a credential that is no longer valid, you will need to redo the above steps. One indicator this is a problem is seeing the text: "The system detected a possible attempt to compromise security. Please ensure that you can contact the server that authenticated you."   Do not forget to re-authenticate with both the IP address and the hostname. -


    - -*/ From baeb8e1590d28d24972fbf41b6eab79f94c2c88f Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Thu, 11 Jul 2019 18:37:20 -0400 Subject: [PATCH 41/69] Add "Prompt before exiting imager" checkbox, default is selected --- .../configuration/Bundle.properties | 2 + .../configuration/Bundle.properties-MERGED | 2 + .../configuration/ConfigVisualPanel2.form | 143 ++++++++++-------- .../configuration/ConfigVisualPanel2.java | 117 ++++++++------ .../configuration/LogicalImagerConfig.java | 27 ++++ .../LogicalImagerConfigDeserializer.java | 8 +- 6 files changed, 187 insertions(+), 112 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/Bundle.properties b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/Bundle.properties index 7eb2002f9e..65fa3dd72d 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/Bundle.properties @@ -120,3 +120,5 @@ ConfigVisualPanel2.flagEncryptionProgramsCheckBox.text=Alert if encryption progr ConfigVisualPanel2.finalizeImageWriter.text=Continue imaging after searches are performed EditNonFullPathsRulePanel.fileNamesInfoLabel.text=File names are case insensitive. EditNonFullPathsRulePanel.extensionsInfoLabel.text=Extensions are case insensitive. +ConfigVisualPanel2.promptBeforeExit.text=Prompt before exiting imager +ConfigVisualPanel2.promptBeforeExit.actionCommand= diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/Bundle.properties-MERGED index 5846a4ad34..3e6aad34de 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/Bundle.properties-MERGED @@ -189,6 +189,8 @@ ConfigVisualPanel2.flagEncryptionProgramsCheckBox.text=Alert if encryption progr ConfigVisualPanel2.finalizeImageWriter.text=Continue imaging after searches are performed EditNonFullPathsRulePanel.fileNamesInfoLabel.text=File names are case insensitive. EditNonFullPathsRulePanel.extensionsInfoLabel.text=Extensions are case insensitive. +ConfigVisualPanel2.promptBeforeExit.text=Prompt before exiting imager +ConfigVisualPanel2.promptBeforeExit.actionCommand= NewRuleSetPanel.attributeRule.description=Search for files based on one or more attributes or metadata fields. NewRuleSetPanel.attributeRule.name=Attribute NewRuleSetPanel.fullPathRule.description=Search for files based on full exact match path. diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel2.form b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel2.form index 9fd42600c8..b5725173df 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel2.form +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel2.form @@ -26,7 +26,7 @@ - + @@ -41,42 +41,19 @@ - - - - - - - - - - - - - - - - - - - - + + - + - - + - - - - - + @@ -85,15 +62,52 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + + + + + @@ -107,16 +121,17 @@ - + - - + + + @@ -140,30 +155,19 @@ - - - - + - + - - - - + - + - - - - - - - - + + + @@ -171,25 +175,27 @@ - + - + - + - - - + + + - + + + + - @@ -220,7 +226,6 @@ - @@ -563,5 +568,19 @@ + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel2.java b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel2.java index 967ed1ff75..d205701701 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel2.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel2.java @@ -110,6 +110,7 @@ final class ConfigVisualPanel2 extends JPanel { minSizeTextField = new javax.swing.JFormattedTextField(); maxSizeLabel = new javax.swing.JLabel(); maxSizeTextField = new javax.swing.JFormattedTextField(); + promptBeforeExit = new javax.swing.JCheckBox(); org.openide.awt.Mnemonics.setLocalizedText(modifiedDateLabel, org.openide.util.NbBundle.getMessage(ConfigVisualPanel2.class, "ConfigVisualPanel2.modifiedDateLabel.text")); // NOI18N @@ -120,7 +121,6 @@ final class ConfigVisualPanel2 extends JPanel { modifiedWithinTextField.setPreferredSize(new java.awt.Dimension(60, 20)); org.openide.awt.Mnemonics.setLocalizedText(daysIncludedLabel, org.openide.util.NbBundle.getMessage(ConfigVisualPanel2.class, "ConfigVisualPanel2.daysIncludedLabel.text")); // NOI18N - daysIncludedLabel.setEnabled(false); org.openide.awt.Mnemonics.setLocalizedText(fullPathsLabel, org.openide.util.NbBundle.getMessage(ConfigVisualPanel2.class, "ConfigVisualPanel2.fullPathsLabel.text")); // NOI18N @@ -255,6 +255,15 @@ final class ConfigVisualPanel2 extends JPanel { maxSizeTextField.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.NumberFormatter(new java.text.DecimalFormat("#,###; ")))); maxSizeTextField.setEnabled(false); + promptBeforeExit.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(promptBeforeExit, org.openide.util.NbBundle.getMessage(ConfigVisualPanel2.class, "ConfigVisualPanel2.promptBeforeExit.text")); // NOI18N + promptBeforeExit.setActionCommand(org.openide.util.NbBundle.getMessage(ConfigVisualPanel2.class, "ConfigVisualPanel2.promptBeforeExit.actionCommand")); // NOI18N + promptBeforeExit.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + promptBeforeExitActionPerformed(evt); + } + }); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -268,7 +277,7 @@ final class ConfigVisualPanel2 extends JPanel { .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addComponent(newRuleButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(editRuleButton) @@ -281,48 +290,57 @@ final class ConfigVisualPanel2 extends JPanel { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(flagEncryptionProgramsCheckBox) - .addComponent(finalizeImageWriter) - .addComponent(shouldSaveCheckBox) - .addComponent(shouldAlertCheckBox) .addComponent(extensionsLabel) .addComponent(filenamesLabel) .addComponent(descriptionLabel) .addComponent(ruleNameLabel) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(modifiedDateLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(fileSizeLabel) - .addComponent(fullPathsLabel) - .addComponent(folderNamesLabel)) - .addGap(4, 4, 4) - .addComponent(minSizeLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(minSizeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 63, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addComponent(maxSizeLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(maxSizeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 63, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGap(107, 107, 107)) + .addComponent(fullPathsLabel) + .addComponent(folderNamesLabel)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() - .addGap(129, 129, 129) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(modifiedWithinTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 54, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(daysIncludedLabel)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(129, 129, 129) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(ruleNameEditTextField, javax.swing.GroupLayout.Alignment.LEADING) .addComponent(descriptionEditTextField, javax.swing.GroupLayout.Alignment.LEADING) .addComponent(extensionsTextField, javax.swing.GroupLayout.Alignment.LEADING) .addComponent(fullPathsScrollPane, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) .addComponent(filenamesScrollPane, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addComponent(folderNamesScrollPane, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)) - .addContainerGap()))))) + .addComponent(folderNamesScrollPane, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(shouldSaveCheckBox) + .addComponent(shouldAlertCheckBox) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(modifiedDateLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(fileSizeLabel)) + .addGap(4, 4, 4) + .addComponent(minSizeLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(minSizeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 63, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18) + .addComponent(maxSizeLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(maxSizeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 63, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addGap(129, 129, 129) + .addComponent(modifiedWithinTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 54, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(daysIncludedLabel))) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()))) .addGroup(layout.createSequentialGroup() .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jSeparator1)))) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(flagEncryptionProgramsCheckBox) + .addComponent(finalizeImageWriter) + .addComponent(promptBeforeExit)) + .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(jSeparator1))))) ); layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {deleteRuleButton, editRuleButton, newRuleButton}); @@ -337,12 +355,13 @@ final class ConfigVisualPanel2 extends JPanel { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(rulesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 479, Short.MAX_VALUE) + .addComponent(rulesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 478, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(newRuleButton) .addComponent(editRuleButton) - .addComponent(deleteRuleButton))) + .addComponent(deleteRuleButton)) + .addContainerGap()) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() @@ -360,23 +379,16 @@ final class ConfigVisualPanel2 extends JPanel { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(filenamesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addComponent(filenamesLabel) - .addGap(0, 0, Short.MAX_VALUE))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(filenamesLabel)) + .addGap(16, 16, 16) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(folderNamesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addComponent(folderNamesLabel) - .addGap(0, 0, Short.MAX_VALUE))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(folderNamesLabel)) + .addGap(18, 18, 18) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(fullPathsLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(fullPathsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addGap(11, 11, 11))) + .addComponent(fullPathsLabel) + .addComponent(fullPathsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) .addComponent(minSizeLabel) .addComponent(minSizeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -392,13 +404,15 @@ final class ConfigVisualPanel2 extends JPanel { .addComponent(shouldSaveCheckBox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(shouldAlertCheckBox) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18) + .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(flagEncryptionProgramsCheckBox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(finalizeImageWriter))) - .addContainerGap()) + .addComponent(finalizeImageWriter) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(promptBeforeExit) + .addGap(21, 21, 21)))) ); }// //GEN-END:initComponents @@ -528,6 +542,10 @@ final class ConfigVisualPanel2 extends JPanel { config.setFinalizeImageWriter(finalizeImageWriter.isSelected()); }//GEN-LAST:event_finalizeImageWriterActionPerformed + private void promptBeforeExitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_promptBeforeExitActionPerformed + config.setPromptBeforeExit(promptBeforeExit.isSelected()); + }//GEN-LAST:event_promptBeforeExitActionPerformed + /** * Set the whether the a rule for detecting encryption programs will be * added to the rules in this config @@ -597,6 +615,7 @@ final class ConfigVisualPanel2 extends JPanel { private javax.swing.JLabel modifiedDateLabel; private javax.swing.JTextField modifiedWithinTextField; private javax.swing.JButton newRuleButton; + private javax.swing.JCheckBox promptBeforeExit; private javax.swing.JTextField ruleNameEditTextField; private javax.swing.JLabel ruleNameLabel; private javax.swing.JLabel ruleSetFileLabel; diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/LogicalImagerConfig.java b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/LogicalImagerConfig.java index 685a2c8d35..9d1c175de3 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/LogicalImagerConfig.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/LogicalImagerConfig.java @@ -38,6 +38,10 @@ class LogicalImagerConfig { @Expose(serialize = true) private boolean finalizeImageWriter; + @SerializedName("prompt-before-exit") + @Expose(serialize = true) + private boolean promptBeforeExit; + @SerializedName("rule-sets") @Expose(serialize = true) private List ruleSets; @@ -45,6 +49,7 @@ class LogicalImagerConfig { LogicalImagerConfig() { this.version = CURRENT_VERSION; this.finalizeImageWriter = false; + this.promptBeforeExit = true; this.ruleSets = new ArrayList<>(); } @@ -54,6 +59,7 @@ class LogicalImagerConfig { ) { this.version = CURRENT_VERSION; this.finalizeImageWriter = finalizeImageWriter; + this.promptBeforeExit = true; this.ruleSets = ruleSets; } @@ -64,6 +70,19 @@ class LogicalImagerConfig { ) { this.version = version; this.finalizeImageWriter = finalizeImageWriter; + this.promptBeforeExit = true; + this.ruleSets = ruleSets; + } + + LogicalImagerConfig( + String version, + boolean finalizeImageWriter, + boolean promptBeforeExit, + List ruleSets + ) { + this.version = version; + this.finalizeImageWriter = finalizeImageWriter; + this.promptBeforeExit = promptBeforeExit; this.ruleSets = ruleSets; } @@ -87,6 +106,14 @@ class LogicalImagerConfig { this.finalizeImageWriter = finalizeImageWriter; } + boolean isPromptBeforeExit() { + return promptBeforeExit; + } + + void setPromptBeforeExit(boolean promptBeforeExit) { + this.promptBeforeExit = promptBeforeExit; + } + List getRuleSets() { return ruleSets; } diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/LogicalImagerConfigDeserializer.java b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/LogicalImagerConfigDeserializer.java index a9c049ce08..83abf4da0d 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/LogicalImagerConfigDeserializer.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/LogicalImagerConfigDeserializer.java @@ -45,6 +45,7 @@ class LogicalImagerConfigDeserializer implements JsonDeserializer parseRules(JsonArray asJsonArray) { From 2af2d50f102ec8ce919f7e6d241f17ea2fb02fbb Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Fri, 12 Jul 2019 09:22:22 -0400 Subject: [PATCH 42/69] Update RegexQuery.java Change so it removes infinite loop with [A-Z]* regex KWS. --- .../org/sleuthkit/autopsy/keywordsearch/RegexQuery.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index 7664e60f8c..c9c5563af5 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -356,6 +356,13 @@ final class RegexQuery implements KeywordSearchQuery { String hit = hitMatcher.group(); + /** + * No need to continue on if the the string is "" nothing to find or do. + */ + if ("".equals(hit)) { + break; + } + offset = hitMatcher.end(); final ATTRIBUTE_TYPE artifactAttributeType = originalKeyword.getArtifactAttributeType(); From 33cabaf1bf35314d5a6ba730c47bd7737347110e Mon Sep 17 00:00:00 2001 From: Joe Ho Date: Fri, 12 Jul 2019 10:20:32 -0400 Subject: [PATCH 43/69] Fix copy and paste error :-( --- .../autopsy/logicalimager/configuration/ConfigVisualPanel2.java | 1 + .../configuration/LogicalImagerConfigDeserializer.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel2.java b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel2.java index d205701701..65fe93fcd3 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel2.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel2.java @@ -644,6 +644,7 @@ final class ConfigVisualPanel2 extends JPanel { private void updatePanel(String configFilePath, LogicalImagerConfig config, String rowSelectionkey) { configFileTextField.setText(configFilePath); finalizeImageWriter.setSelected(config.isFinalizeImageWriter()); + promptBeforeExit.setSelected(config.isPromptBeforeExit()); LogicalImagerRuleSet ruleSet = getRuleSetFromCurrentConfig(); flagEncryptionProgramsCheckBox.setSelected(ruleSet.find(EncryptionProgramsRule.getName()) != null); RulesTableModel rulesTableModel = new RulesTableModel(); diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/LogicalImagerConfigDeserializer.java b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/LogicalImagerConfigDeserializer.java index 83abf4da0d..dd433b68cf 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/LogicalImagerConfigDeserializer.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/LogicalImagerConfigDeserializer.java @@ -58,7 +58,7 @@ class LogicalImagerConfigDeserializer implements JsonDeserializer Date: Fri, 12 Jul 2019 13:19:47 -0400 Subject: [PATCH 44/69] Addressed comments --- docs/doxygen-user/central_repo.dox | 4 ++-- .../multi-user/installActiveMQ.dox | 2 +- .../multi-user/installMultiUser.dox | 21 +++++++------------ .../multi-user/installMultiUserClient.dox | 4 ++-- .../multi-user/installPostgres.dox | 2 +- .../multi-user/installSharedStorage.dox | 4 ++-- docs/doxygen-user/multi-user/installSolr.dox | 8 ++----- .../multi-user/installSystems.dox | 17 +++++++++------ docs/doxygen-user/multi-user/installUsers.dox | 4 ++-- 9 files changed, 31 insertions(+), 35 deletions(-) diff --git a/docs/doxygen-user/central_repo.dox b/docs/doxygen-user/central_repo.dox index 03b45b6040..5583971bfc 100644 --- a/docs/doxygen-user/central_repo.dox +++ b/docs/doxygen-user/central_repo.dox @@ -38,7 +38,7 @@ Once a database has been configured, the lower two buttons on the main panel wil Setting up PostgreSQL Deployment -If needed, see the \ref install_postgresql for help setting up your PostgreSQL server. +If needed, see the \ref install_postgresql_page for help setting up your PostgreSQL server. For PostgreSQL all values are required, but some defaults are provided for convenience. @@ -221,4 +221,4 @@ then contain each instance of that notable phone number. -*/ \ No newline at end of file +*/ diff --git a/docs/doxygen-user/multi-user/installActiveMQ.dox b/docs/doxygen-user/multi-user/installActiveMQ.dox index fd0726e1b1..8a0d9405a2 100644 --- a/docs/doxygen-user/multi-user/installActiveMQ.dox +++ b/docs/doxygen-user/multi-user/installActiveMQ.dox @@ -1,4 +1,4 @@ -/*! \page install_activemq Install and Configure ActiveMQ +/*! \page install_activemq_page Install and Configure ActiveMQ \section install_activemq_overview Overview diff --git a/docs/doxygen-user/multi-user/installMultiUser.dox b/docs/doxygen-user/multi-user/installMultiUser.dox index 7a67bd6ade..a70d2b5deb 100644 --- a/docs/doxygen-user/multi-user/installMultiUser.dox +++ b/docs/doxygen-user/multi-user/installMultiUser.dox @@ -16,25 +16,20 @@ Each Autopsy client will then use those shared resources instead of the embedded \section multiuser_install_install Cluster Installation and Configuration -Let's now step through the process of setting up an Autopsy cluster. +Let's now step through the process of setting up an Autopsy cluster. When you setup the network services, write down the addresses, user names, and passwords for each so that you can more easily configure each of the client systems afterwards. -Step 1: \ref multiuser_system +Step 1: \ref install_multiuser_systems_page -Step 2: \ref multiuser_users +Step 2: \ref install_multiuseruser_page -Step 3: \ref multiuser_storage +Step 3: \ref install_multiuser_storage_page -Step 4: \ref install_postgresql +Step 4: \ref install_postgresql_page -Step 5: \ref install_solr +Step 5: \ref install_solr_page -Step 6: \ref install_activemq - -Step 7: \ref multiuser_install_clients - - - -When you setup the above services, write down the addresses, user names, and passwords or each so that you can configure each of the client systems afterwards. +Step 6: \ref install_activemq_page +Step 7: \ref install_multiuserclient_page */ diff --git a/docs/doxygen-user/multi-user/installMultiUserClient.dox b/docs/doxygen-user/multi-user/installMultiUserClient.dox index d0f3fdeb6a..171875e4a3 100644 --- a/docs/doxygen-user/multi-user/installMultiUserClient.dox +++ b/docs/doxygen-user/multi-user/installMultiUserClient.dox @@ -1,7 +1,7 @@ -/*! \page install_multiuserclient_page Setting Up Multi-user Client +/*! \page install_multiuserclient_page Install Autopsy Clients -\section multiuser_install_clients Install Autopsy Clients +\section multiuser_install_clients Overview Once the infrastructure is in place, you can configure Autopsy clients to use them. - Install Autopsy on each client system. Use the normal installer and pick the defaults. diff --git a/docs/doxygen-user/multi-user/installPostgres.dox b/docs/doxygen-user/multi-user/installPostgres.dox index 1f089c781b..0164a1fd1a 100644 --- a/docs/doxygen-user/multi-user/installPostgres.dox +++ b/docs/doxygen-user/multi-user/installPostgres.dox @@ -1,4 +1,4 @@ -/*! \page install_postgresql Install and Configure PostgreSQL +/*! \page install_postgresql_page Install and Configure PostgreSQL \section install_post_over Overview diff --git a/docs/doxygen-user/multi-user/installSharedStorage.dox b/docs/doxygen-user/multi-user/installSharedStorage.dox index 3152ed3ab0..6651d6ab69 100644 --- a/docs/doxygen-user/multi-user/installSharedStorage.dox +++ b/docs/doxygen-user/multi-user/installSharedStorage.dox @@ -1,6 +1,6 @@ -/*! \page install_multiuser_storage_page Setting Up Shared Storage +/*! \page install_multiuser_storage_page Set Up Shared Storage -\section multiuser_storage Set Up Shared Storage +\section multiuser_storage Overview The cluster will need shared storage that can be accessed from: - Autopsy clients diff --git a/docs/doxygen-user/multi-user/installSolr.dox b/docs/doxygen-user/multi-user/installSolr.dox index 6070fc65e1..c5ffa052bd 100644 --- a/docs/doxygen-user/multi-user/installSolr.dox +++ b/docs/doxygen-user/multi-user/installSolr.dox @@ -1,4 +1,4 @@ -/*! \page install_solr Install and Configure Solr +/*! \page install_solr_page Install and Configure Solr \section install_solr_overview Overview @@ -15,12 +15,8 @@ We use Bitnami Solr, which packages Solr as a Windows service. You will need: - A 64-bit version of the Java Runtime Environment (JRE) from http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html. - The Apache Solr 4.10.3-0 installation package. This is no longer available from its original source, but you can find it on our site: https://sourceforge.net/projects/autopsy/files/CollaborativeServices/Solr. +-- NOTE: We tested Solr 6 at one point, but ran into stability problems when loading and unloading cores. For now, you need to use Solr 4. - An installed version of Autopsy so that you can copy files from it. You can install Autopsy on one of the planned client systems. You do not need to install it on the Solr server. - - - - - - A network-accessible machine to install Solr on. Note that the Solr process will need to write data out to the main shared storage drive, and needs adequate permissions to write to this location, which may be across a network. diff --git a/docs/doxygen-user/multi-user/installSystems.dox b/docs/doxygen-user/multi-user/installSystems.dox index 400ecd6dd3..e171539245 100644 --- a/docs/doxygen-user/multi-user/installSystems.dox +++ b/docs/doxygen-user/multi-user/installSystems.dox @@ -1,6 +1,6 @@ -/*! \page install_multiuser_systems_page Multi-user Hardware / VM Selection +/*! \page install_multiuser_systems_page Pick Your Hardware / VM Configuration -\section multiuser_system Pick Your Hardware / VM Configuration +\section multiuser_system Overview The first step in setting up a multi-user cluster is picking how many computers or VMs you'll use to run the various services. @@ -10,14 +10,19 @@ From a service perspective, you'll need to run: - ActiveMQ messaging server - Network storage -You can run each of these on their own dedicated VM, but that is not necessary. We recommend: +You can run each of these on their own dedicated VM, but that is not necessary. + +Apache Solr uses a lot of memory, so we recommend keeping it by itself. The exception is if you are using Windows File Sharing for shared storage. You can get better Solr performance if it is writing to local storage instead of over the network. So, you can consider using the same computer for both Solr and shared storage. + +Also note that because all computers need to access the shared storage at the same path, you cannot mix operating systems. A Linux system running Solr will not be able to access the shared storage at the same path as a Windows Autopsy client. + + + +We recommend: - Server 1: PostgreSQL and ActiveMQ - Both of these are low overhead services. - Server 2: Apache Solr and Shared Storage (if you are using Windows File Sharing). - - Apache Solr uses a lot of memory, so we keep it by itself. - - The benefit of running the shared storage on the same computer as Solr is performance. Solr is much faster when it can write to local storage instead of over the network. - - If you are using a different form of shared storage, then we still recommend keeping Solr on its own. \subsection multiuser_system_hw Suggested Hardware diff --git a/docs/doxygen-user/multi-user/installUsers.dox b/docs/doxygen-user/multi-user/installUsers.dox index 1b29230258..53a556ece8 100644 --- a/docs/doxygen-user/multi-user/installUsers.dox +++ b/docs/doxygen-user/multi-user/installUsers.dox @@ -1,6 +1,6 @@ -/*! \page install_multiuseruser_page Picking Users +/*! \page install_multiuseruser_page Pick Your User Accounts -\section multiuser_users Pick Your User Accounts +\section multiuser_users Overview Before you get into configuring any computers, you should have an understanding about what user accounts will be used. User account permissions are one of the most common challenges people have when setting up a cluster. From 4d0f8bbb4f19028ac61a99f0b9f8023ab8408932 Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 15 Jul 2019 13:36:31 -0400 Subject: [PATCH 45/69] Updates to Solr configuration procedures. --- docs/doxygen-user/multi-user/installSolr.dox | 21 ++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/doxygen-user/multi-user/installSolr.dox b/docs/doxygen-user/multi-user/installSolr.dox index c5ffa052bd..9ef8e3c907 100644 --- a/docs/doxygen-user/multi-user/installSolr.dox +++ b/docs/doxygen-user/multi-user/installSolr.dox @@ -4,9 +4,9 @@ Autopsy uses Apache Solr to store keyword text indexes. A central server is needed in a multi-user cluster to maintain and search the indexes. -A new Solr core is created for each case and the core/index is stored in the case folder (not on the local drive of the Solr server). +A new text index is created for each case and is stored in the case folder on shared storage (not on the local drive of the Solr server). -Solr's embedded Zookeeper is also used as a coordination service for Autopsy. +Solr's embedded ZooKeeper is also used as a coordination service for Autopsy. \section install_solr_prereq Prerequisites @@ -97,7 +97,7 @@ Because we made changes to the service configuration, we need to reinstall it.

    -You should be able to see the Solr service in a web browser via the URL http://localhost:8983/solr/#/ as shown in the screenshot below. If you can, you should skip the next step. If you cannot, proceed to the next step. +At this point you should be able to access the Solr admin panel in a web browser via the URL http://localhost:8983/solr/#/ \subsection install_solr_service_user Configure Service User @@ -117,25 +117,26 @@ Back in \ref install_multiuseruser_page, you should have decided what user to ru \section install_solr_test Testing -There are two tests that you should do before declaring that Solr has been configured. +There are two tests that you should perform to confirm that the Solr machine is configured correctly. -- Web Interface: You should be able to see the Solr admin panel in a web browser via the URL http://localhost:8983/solr/#/. +- Web Interface: You should attempt to access the Solr admin panel in a web browser from another machine on the network. Replace the IP address in the following URL with the IP address or the host name that the Solr service is running on: http://172.16.12.61:8983/solr/#/.

    \image html solrinstall3.PNG

    -If the service is appropriately started and you are unable to see the screenshot above, then it could be that port 8983 is blocked by your firewall. Contact your network administrator to open this port. +If the service is appropriately started but you are unable to see the screenshot above, then it could be that port 8983 for Solr and port 9983 for ZooKeeper are blocked by your firewall. Contact your network administrator to open these ports. -- Shared Storage: If you are running Solr as a normal user, then log into the Solr computer as that user and and access the shared storage paths. Ensure that you can access the UNC paths (or drive letters if you have hardware NAS). If you are prompted for a password to access the shared storage, then either enter the password and choose to save the credentials or reconfigure the setup so that the same passwords are used, etc. See \ref multiuser_users_store for steps on storing credentials. If you needed to store the credentials, then you should restart the service or reboot the computer (we have observed that a running service does not get the updated credentials). +- Shared Storage: Log into the Solr computer as the user you decided to run the Solr service as and attempt to access the shared storage paths. Ensure that you can access the UNC paths (or drive letters if you have hardware NAS). If everything is configured correctly you should be able to access the storage paths without having to provide credentials. +If you are prompted for a password to access the shared storage, then either enter the password and choose to save the credentials or reconfigure the setup so that the same passwords are used, etc. See \ref multiuser_users_store for steps on storing credentials. If you needed to store the credentials, then you should restart the service or reboot the computer (we have observed that a running service does not get the updated credentials). NOTE: You can not do a full test of permissions until you make a test case after all of the services are configured. \section install_solr_backup Backing Up Solr creates two types of data that need to be backed up: -- Text Indexes: These are stored in the case folder on the shared storage and should get backed up. -- ZooKeeper Data: Autopsy uses a service called ZooKeeper that Solr runs and it stores data about what cases exist and who has them open. You should back this data up so that you can have a list of all available multi-user cases. - - That data is stored in X. +- Text Indexes: These are stored in the case folder on the shared storage. +- ZooKeeper Data: Autopsy uses a service called ZooKeeper embedded in Solr that stores data about what cases exist and who has them open. This data needs to be backed up so that you can have a list of all available multi-user cases. + - In a default installation that data is stored in C:\\Bitnami\\solr-4.10.3-0\\apache-solr\\solr\\zoo_data. */ From d70b1723cce5f5fb97e8abf340425663fd3299a4 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 15 Jul 2019 13:55:44 -0400 Subject: [PATCH 46/69] addressed review comments --- .../autopsy/thunderbirdparser/MboxParser.java | 103 +++---- .../autopsy/thunderbirdparser/PstParser.java | 252 ++++++++++-------- .../ThunderbirdMboxFileIngestModule.java | 7 - 3 files changed, 187 insertions(+), 175 deletions(-) diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java index 6604c2c623..7c3d74121b 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java @@ -62,9 +62,9 @@ import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.EncodedFileOutputStream; /** - * An Iterator for parsing mbox files. Wraps an instance of MBoxEmailIterator. + * An Iterator for parsing mbox files. Wraps an instance of MBoxEmailIterator. */ -class MboxParser implements Iterator{ +class MboxParser implements Iterator { private static final Logger logger = Logger.getLogger(MboxParser.class.getName()); private final DefaultMessageBuilder messageBuilder; @@ -79,11 +79,10 @@ class MboxParser implements Iterator{ * The local path of the mbox file. */ private String localPath; - - private Iterator emailIterator = null; - - private MboxParser( String localPath) { + private Iterator emailIterator = null; + + private MboxParser(String localPath) { this.localPath = localPath; messageBuilder = new DefaultMessageBuilder(); MimeConfig config = MimeConfig.custom().setMaxLineLen(-1).build(); @@ -95,13 +94,15 @@ class MboxParser implements Iterator{ static boolean isValidMimeTypeMbox(byte[] buffer) { return (new String(buffer)).startsWith("From "); //NON-NLS } - + /** - * Returns an instance of MBoxParser that will iterate and return EMailMessage - * objects with only the information needed for threading emails. - * + * Returns an instance of MBoxParser that will iterate and return + * EMailMessage objects with only the information needed for threading + * emails. + * * @param localPath String path to the mboxFile - * @param mboxFile The mboxFile to parse + * @param mboxFile The mboxFile to parse + * * @return Instance of MboxParser */ static MboxParser getThreadInfoIterator(String localPath, File mboxFile) { @@ -109,40 +110,42 @@ class MboxParser implements Iterator{ parser.createIterator(mboxFile, 0, false); return parser; } - + /** - * Returns an instance of MBoxParser that will iterate "whole" EmailMessages. - * + * Returns an instance of MBoxParser that will iterate "whole" + * EmailMessages. + * * @param localPath String path to the mboxFile - * @param mboxFile The mboxFile to parse - * @param fileID The fileID of the abstractFile that mboxFile was found + * @param mboxFile The mboxFile to parse + * @param fileID The fileID of the abstractFile that mboxFile was found + * * @return Instance of MboxParser */ static MboxParser getEmailIterator(String localPath, File mboxFile, long fileID) { MboxParser parser = new MboxParser(localPath); parser.createIterator(mboxFile, fileID, true); - + return parser; } - + /** * Creates the real Iterator object instance. - * - * @param mboxFile The mboxFile to parse - * @param fileID The fileID of the abstractFile that mboxFile was found - * @param wholeMsg True if EmailMessage should have the whole message, - * not just the thread information. + * + * @param mboxFile The mboxFile to parse + * @param fileID The fileID of the abstractFile that mboxFile was found + * @param wholeMsg True if EmailMessage should have the whole message, not + * just the thread information. */ private void createIterator(File mboxFile, long fileID, boolean wholeMsg) { // Detect possible charsets List encoders = getPossibleEncoders(mboxFile); - + // Loop through the possible encoders and find the first one that works. // That will usually be one of the first ones. for (CharsetEncoder encoder : encoders) { try { Iterable mboxIterable = MboxIterator.fromFile(mboxFile).charset(encoder.charset()).build(); - if(mboxIterable != null) { + if (mboxIterable != null) { emailIterator = new MBoxEmailIterator(mboxIterable.iterator(), encoder, fileID, wholeMsg); } break; @@ -156,12 +159,12 @@ class MboxParser implements Iterator{ } } } - + @Override public boolean hasNext() { return emailIterator != null ? emailIterator.hasNext() : false; } - + @Override public EmailMessage next() { return emailIterator != null ? emailIterator.next() : null; @@ -190,7 +193,7 @@ class MboxParser implements Iterator{ email.setSentDate(msg.getDate()); email.setLocalPath(localPath); email.setMessageID(msg.getMessageId()); - + Field field = msg.getHeader().getField("in-reply-to"); //NON-NLS String inReplyTo = null; @@ -219,23 +222,23 @@ class MboxParser implements Iterator{ } else { handleTextBody(email, (TextBody) msg.getBody(), msg.getMimeType(), msg.getHeader().getFields()); } - + return email; } - + /** - * Extract the subject, inReplyTo, message-ID and references from the Message - * object and returns them in a new EmailMessage object. - * + * Extract the subject, inReplyTo, message-ID and references from the + * Message object and returns them in a new EmailMessage object. + * * @param msg Message object - * - * @return EmailMessage instance with only some of the message information + * + * @return EmailMessage instance with only some of the message information */ private EmailMessage extractPartialEmail(Message msg) { EmailMessage email = new EmailMessage(); email.setSubject(msg.getSubject()); email.setMessageID(msg.getMessageId()); - + Field field = msg.getHeader().getField("in-reply-to"); //NON-NLS String inReplyTo = null; @@ -257,7 +260,7 @@ class MboxParser implements Iterator{ email.setReferences(references); } - + return email; } @@ -295,7 +298,7 @@ class MboxParser implements Iterator{ * * @param email * @param tb - * @param type The Mime type of the body. + * @param type The Mime type of the body. */ private void handleTextBody(EmailMessage email, TextBody tb, String type, List fields) { BufferedReader r; @@ -307,16 +310,16 @@ class MboxParser implements Iterator{ while ((line = r.readLine()) != null) { bodyString.append(line).append("\n"); } - + headersString.append("\n-----HEADERS-----\n"); - for(Field field: fields) { + for (Field field : fields) { String nextLine = field.getName() + ": " + field.getBody(); headersString.append("\n").append(nextLine); } headersString.append("\n\n---END HEADERS--\n\n"); email.setHeaders(headersString.toString()); - + switch (type) { case ContentTypeField.TYPE_TEXT_PLAIN: email.setTextBody(bodyString.toString()); @@ -340,7 +343,7 @@ class MboxParser implements Iterator{ * @param email * @param e */ - @NbBundle.Messages ({"MboxParser.handleAttch.noOpenCase.errMsg=Exception while getting open case."}) + @NbBundle.Messages({"MboxParser.handleAttch.noOpenCase.errMsg=Exception while getting open case."}) private void handleAttachment(EmailMessage email, Entity e, long fileID, int index) { String outputDirPath; String relModuleOutputPath; @@ -350,7 +353,7 @@ class MboxParser implements Iterator{ } catch (NoCurrentCaseException ex) { addErrorMessage(Bundle.MboxParser_handleAttch_noOpenCase_errMsg()); logger.log(Level.SEVERE, Bundle.MboxParser_handleAttch_noOpenCase_errMsg(), ex); //NON-NLS - return; + return; } String filename = e.getFilename(); @@ -498,17 +501,17 @@ class MboxParser implements Iterator{ private void addErrorMessage(String msg) { errors.append("
  • ").append(msg).append("
  • "); //NON-NLS } - + /** * An Interator for mbox email messages. */ final class MBoxEmailIterator implements Iterator { - + private final Iterator mboxIterator; private final CharsetEncoder encoder; private final long fileID; private final boolean wholeMsg; - + MBoxEmailIterator(Iterator mboxIter, CharsetEncoder encoder, long fileID, boolean wholeMsg) { mboxIterator = mboxIter; this.encoder = encoder; @@ -524,12 +527,12 @@ class MboxParser implements Iterator{ @Override public EmailMessage next() { CharBufferWrapper messageBuffer = mboxIterator.next(); - + try { Message msg = messageBuilder.parseMessage(messageBuffer.asInputStream(encoder.charset())); - if(wholeMsg) { + if (wholeMsg) { return extractEmail(msg, fileID); - } else { + } else { return extractPartialEmail(msg); } } catch (RuntimeException | IOException ex) { @@ -537,6 +540,6 @@ class MboxParser implements Iterator{ } return null; } - + } } diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java index 32ab28352e..d8a8932536 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java @@ -62,14 +62,14 @@ class PstParser { * directory structure. */ private final StringBuilder errors; - + private final IngestServices services; - + private PSTFile pstFile; private long fileID; - + private int failureCount = 0; - + PstParser(IngestServices services) { errors = new StringBuilder(); this.services = services; @@ -79,30 +79,33 @@ class PstParser { OK, ERROR, ENCRYPT; } + /** - * Create an instance of PSTFile for the given File object. - * - * The constructor for PSTFile object will throw a generic PSTException if the - * file is encrypted. - *
    PSTFile.java - * - * @param file File to open + * Create an instance of PSTFile for the given File object. + * + * The constructor for PSTFile object will throw a generic PSTException if + * the file is encrypted. + * + * PSTFile.java + * + * @param file File to open * @param fileID File id for use when creating the EmailMessage objects - * @return ParserResult value OK if the PSTFile was successfully created, - * ENCRYPT will be returned for PSTExceptions that matches at specific - * message or IllegalArgumentExceptions + * + * @return ParserResult value OK if the PSTFile was successfully created, + * ENCRYPT will be returned for PSTExceptions that matches at + * specific message or IllegalArgumentExceptions */ ParseResult open(File file, long fileID) { - if(file == null) { + if (file == null) { return ParseResult.ERROR; } - + try { pstFile = new PSTFile(file); - } catch(PSTException ex) { + } catch (PSTException ex) { // This is the message thrown from the PSTFile constructor if it // detects that the file is encrypted. - if(ex.getMessage().equals("Only unencrypted and compressable PST files are supported at this time")) { //NON-NLS + if (ex.getMessage().equals("Only unencrypted and compressable PST files are supported at this time")) { //NON-NLS logger.log(Level.INFO, "Found encrypted PST file."); //NON-NLS return ParseResult.ENCRYPT; } @@ -117,140 +120,148 @@ class PstParser { logger.log(Level.INFO, "Found encrypted PST file."); //NON-NLS return ParseResult.ENCRYPT; } - + return ParseResult.OK; } - + /** - * Creates an EmailMessage iterator for pstFile. These Email objects will be + * Creates an EmailMessage iterator for pstFile. These Email objects will be * complete and with all available information. - * + * * @return A instance of an EmailMessage Iterator */ Iterator getEmailMessageIterator() { - if(pstFile == null) { + if (pstFile == null) { return null; } - + Iterable iterable = null; - + try { iterable = getEmaiMessageIterator(pstFile.getRootFolder(), "\\", fileID, true); } catch (PSTException | IOException ex) { logger.log(Level.WARNING, String.format("Exception thrown while parsing fileID: %d", fileID), ex); } - - if(iterable == null) { + + if (iterable == null) { return null; } - + return iterable.iterator(); } - + /** * Get a List of EmailMessages which contain only the information needed for * threading the emails. - * + * * @return A list of EmailMessage or an empty list if non were found. */ List getPartialEmailMessages() { List messages = new ArrayList<>(); Iterator iterator = getPartialEmailMessageIterator(); - if(iterator != null) { - while(iterator.hasNext()) { + if (iterator != null) { + while (iterator.hasNext()) { messages.add(iterator.next()); } } - + return messages; } - + /** - * - * @return + * Returns string containing the list of the current parse errors + * + * @return String error list, empty string if no errors exist. */ String getErrors() { return errors.toString(); } - + + /** + * Returns the count of parse errors. + * + * @return Integer count of parse errors. + */ int getFailureCount() { return failureCount; } - - /** - * Get an Iterator to which will iterate over the PSTFile, but return EmailMessages - * with only the information needed for putting the emails into threads. - * + + /** + * Get an Iterator which will iterate over the PSTFile, but return + * EmailMessages with only the information needed for putting the emails + * into threads. + * * @return A EmailMessage iterator or null if no messages where found */ private Iterator getPartialEmailMessageIterator() { - if(pstFile == null) { + if (pstFile == null) { return null; } - + Iterable iterable = null; - + try { iterable = getEmaiMessageIterator(pstFile.getRootFolder(), "\\", fileID, false); } catch (PSTException | IOException ex) { logger.log(Level.WARNING, String.format("Exception thrown while parsing fileID: %d", fileID), ex); } - - if(iterable == null) { + + if (iterable == null) { return null; } - + return iterable.iterator(); } /** * Creates an Iterable object of Email messages for the given folder. - * - * @param folder PSTFolder to process - * @param path String path to folder - * @param fileID FileID of the AbstractFile folder was found in + * + * @param folder PSTFolder to process + * @param path String path to folder + * @param fileID FileID of the AbstractFile folder was found in * @param partialEmail Whether or not fill the EMailMessage with all data - * - * @return An Iterable for iterating email message, or null if there were no - * messages or children in folder. - * + * + * @return An Iterable for iterating email message, or null if there were no + * messages or children in folder. + * * @throws PSTException - * @throws IOException + * @throws IOException */ private Iterable getEmaiMessageIterator(PSTFolder folder, String path, long fileID, boolean wholeMsg) throws PSTException, IOException { Iterable iterable = null; - - if(folder.getContentCount() > 0) { + + if (folder.getContentCount() > 0) { iterable = new PstEmailIterator(folder, path, fileID, wholeMsg).getIterable(); } - - if(folder.hasSubfolders()) { + + if (folder.hasSubfolders()) { List subFolders = folder.getSubFolders(); - for(PSTFolder subFolder: subFolders) { + for (PSTFolder subFolder : subFolders) { String newpath = path + "\\" + subFolder.getDisplayName(); Iterable subIterable = getEmaiMessageIterator(subFolder, newpath, fileID, wholeMsg); - if(subIterable == null) { + if (subIterable == null) { continue; } - - if(iterable != null) { - iterable = Iterables.concat(iterable, subIterable); + + if (iterable != null) { + iterable = Iterables.concat(iterable, subIterable); } else { iterable = subIterable; } - + } } - + return iterable; } + /** * Create an EmailMessage from a PSTMessage. * - * @param msg - * @param localPath + * @param msg PSTMessage object to parse + * @param localPath Path to local file * - * @return + * @return EmailMessage object. */ private EmailMessage extractEmailMessage(PSTMessage msg, String localPath, long fileID) { EmailMessage email = new EmailMessage(); @@ -260,10 +271,10 @@ class PstParser { email.setSender(getSender(msg.getSenderName(), msg.getSenderEmailAddress())); email.setSentDate(msg.getMessageDeliveryTime()); email.setTextBody(msg.getBody()); - if(false == msg.getTransportMessageHeaders().isEmpty()) { + if (false == msg.getTransportMessageHeaders().isEmpty()) { email.setHeaders("\n-----HEADERS-----\n\n" + msg.getTransportMessageHeaders() + "\n\n---END HEADERS--\n\n"); - } - + } + email.setHtmlBody(msg.getBodyHTML()); String rtf = ""; try { @@ -276,14 +287,14 @@ class PstParser { email.setSubject(msg.getSubject()); email.setId(msg.getDescriptorNodeId()); email.setMessageID(msg.getInternetMessageId()); - + String inReplyToID = msg.getInReplyToId(); email.setInReplyToID(inReplyToID); if (msg.hasAttachments()) { extractAttachments(email, msg, fileID); } - + List references = extractReferences(msg.getTransportMessageHeaders()); if (inReplyToID != null && !inReplyToID.isEmpty()) { if (references == null) { @@ -297,12 +308,13 @@ class PstParser { return email; } - + /** - * Create an EmailMessage from a PSTMessage with only the information - * needed for threading emails. + * Create an EmailMessage from a PSTMessage with only the information needed + * for threading emails. * - * @return EmailMessage object with only some information, not all of the msg. + * @return EmailMessage object with only some information, not all of the + * msg. */ private EmailMessage extractPartialEmailMessage(PSTMessage msg) { EmailMessage email = new EmailMessage(); @@ -328,8 +340,8 @@ class PstParser { /** * Add the attachments within the PSTMessage to the EmailMessage. * - * @param email - * @param msg + * @param email EmailMessage object to have attachment added + * @param msg PSTMessage object with the attachments */ @NbBundle.Messages({"PstParser.noOpenCase.errMsg=Exception while getting open case."}) private void extractAttachments(EmailMessage email, PSTMessage msg, long fileID) { @@ -338,8 +350,8 @@ class PstParser { try { outputDirPath = ThunderbirdMboxFileIngestModule.getModuleOutputPath() + File.separator; } catch (NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS - return; + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return; } for (int x = 0; x < numberOfAttachments; x++) { String filename = ""; @@ -390,16 +402,14 @@ class PstParser { /** * Extracts a PSTAttachment to the module output directory. * - * @param attach - * @param outPath - * - * @return + * @param attach PSTAttachment object to be parsed + * @param outPath Location to write attachments * * @throws IOException * @throws PSTException */ private void saveAttachmentToDisk(PSTAttachment attach, String outPath) throws IOException, PSTException { - try (InputStream attachmentStream = attach.getFileInputStream(); + try (InputStream attachmentStream = attach.getFileInputStream(); EncodedFileOutputStream out = new EncodedFileOutputStream(new FileOutputStream(outPath), TskData.EncodingType.XOR1)) { // 8176 is the block size used internally and should give the best performance int bufferSize = 8176; @@ -464,10 +474,15 @@ class PstParser { } } + /** + * Adds passed in string to the error message with formatting. + * + * @param msg String message to add + */ private void addErrorMessage(String msg) { errors.append("
  • ").append(msg).append("
  • "); //NON-NLS } - + /** * Returns the references value from the email header. * @@ -501,7 +516,7 @@ class PstParser { } /** - * A iterator for processing the PST email folder structure and returning + * A iterator for processing the PST email folder structure and returning * instances of the EmailMessage object. */ private final class PstEmailIterator implements Iterator { @@ -509,41 +524,42 @@ class PstParser { private final PSTFolder folder; private EmailMessage current; private EmailMessage next; - + private final String currentPath; private final long fileID; private final boolean wholeMsg; - + /** * Class constructor, initializes the "next" message; - * + * * @param folder PSTFolder object to iterate across - * @param path String path value to the location of folder - * @param fileID Long fileID of the abstract file this PSTFolder was found + * @param path String path value to the location of folder + * @param fileID Long fileID of the abstract file this PSTFolder was + * found */ PstEmailIterator(PSTFolder folder, String path, long fileID, boolean wholeMsg) { this.folder = folder; this.fileID = fileID; this.currentPath = path; this.wholeMsg = wholeMsg; - - if(folder.getContentCount() > 0) { + + if (folder.getContentCount() > 0) { try { - PSTMessage message = (PSTMessage)folder.getNextChild(); - if(message != null) { - if(wholeMsg) { + PSTMessage message = (PSTMessage) folder.getNextChild(); + if (message != null) { + if (wholeMsg) { next = extractEmailMessage(message, currentPath, fileID); } else { next = extractPartialEmailMessage(message); } } - } catch (PSTException | IOException ex) { + } catch (PSTException | IOException ex) { failureCount++; logger.log(Level.WARNING, String.format("Unable to extract emails for path: %s file ID: %d ", path, fileID), ex); } - } + } } - + @Override public boolean hasNext() { return next != null; @@ -551,13 +567,13 @@ class PstParser { @Override public EmailMessage next() { - + current = next; - + try { - PSTMessage message = (PSTMessage)folder.getNextChild(); - if(message != null) { - if(wholeMsg) { + PSTMessage message = (PSTMessage) folder.getNextChild(); + if (message != null) { + if (wholeMsg) { next = extractEmailMessage(message, currentPath, fileID); } else { next = extractPartialEmailMessage(message); @@ -565,28 +581,28 @@ class PstParser { } else { next = null; } - } catch (PSTException | IOException ex) { + } catch (PSTException | IOException ex) { logger.log(Level.WARNING, String.format("Unable to extract emails for path: %s file ID: %d ", currentPath, fileID), ex); failureCount++; next = null; - } - + } + return current; } - + /** * Get a wrapped Iterable version of PstEmailIterator - * + * * @return Iterable wrapping this class */ - Iterable getIterable(){ - return new Iterable(){ + Iterable getIterable() { + return new Iterable() { @Override public Iterator iterator() { return PstEmailIterator.this; } }; } - - } + + } } diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java index b413400404..b91841aa94 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java @@ -225,13 +225,6 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS } -// String errors = parser.getErrors(); -// if (errors.isEmpty() == false) { -// postErrorMessage( -// NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg2", -// abstractFile.getName()), errors); -// } - return ProcessResult.OK; } From c6939887496de88d244f3022d7a7acb552e40378 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 15 Jul 2019 14:53:57 -0400 Subject: [PATCH 47/69] addressed review comments --- .../relationships/ContactDetailsPane.java | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java index 5dba78cd5e..beec25a0e0 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java @@ -18,10 +18,15 @@ */ package org.sleuthkit.autopsy.communications.relationships; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; import java.util.logging.Level; +import javax.imageio.ImageIO; import javax.swing.ImageIcon; import org.openide.explorer.ExplorerManager; import org.openide.nodes.Node; +import org.openide.util.Exceptions; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -63,7 +68,7 @@ public final class ContactDetailsPane extends javax.swing.JPanel implements Expl BlackboardArtifact n = nodes[0].getLookup().lookup(BlackboardArtifact.class); if(n != null) { - nameLabel.setIcon(getImageForFromArtifact(n)); + nameLabel.setIcon(getImageFromArtifact(n)); } } else { nameLabel.setText(""); @@ -77,23 +82,39 @@ public final class ContactDetailsPane extends javax.swing.JPanel implements Expl return explorerManager; } - public ImageIcon getImageForFromArtifact(BlackboardArtifact artifact){ - ImageIcon image = defaultImage; + public ImageIcon getImageFromArtifact(BlackboardArtifact artifact){ + ImageIcon imageIcon = defaultImage; + + if(artifact == null) { + return imageIcon; + } + + BlackboardArtifact.ARTIFACT_TYPE artifactType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); + if(artifactType != BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT) { + return imageIcon; + } try { for(Content content: artifact.getChildren()) { if(content instanceof AbstractFile) { AbstractFile file = (AbstractFile)content; - file.getLocalAbsPath(); - image = new ImageIcon(file.getLocalAbsPath()); + try { + BufferedImage image = ImageIO.read(new File(file.getLocalAbsPath())); + imageIcon = new ImageIcon(image); + break; + } catch (IOException ex) { + // ImageIO.read will through an IOException if file is not an image + // therefore we don't need to report this exception just try + // the next file. + } } } } catch (TskCoreException ex) { logger.log(Level.WARNING, String.format("Unable to load image for contact: %d", artifact.getId()), ex); } - return image; + return imageIcon; } /** From 52460a78c1177521b53221d2d5fa907759bb1b9a Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 15 Jul 2019 15:09:43 -0400 Subject: [PATCH 48/69] modified tooltip text based on review comments --- .../autopsy/communications/Bundle.properties | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 81e765e145..1b2dd9ae37 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -22,13 +22,13 @@ CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualiz CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize VisualizationPanel.fitGraphButton.text= VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin. -VisualizationPanel.fitZoomButton.toolTipText=Fit Visualization +VisualizationPanel.fitZoomButton.toolTipText=Fit visualization to available space. VisualizationPanel.fitZoomButton.text= -VisualizationPanel.zoomActualButton.toolTipText=Reset Zoom +VisualizationPanel.zoomActualButton.toolTipText=Reset visualization default zoom state. VisualizationPanel.zoomActualButton.text= -VisualizationPanel.zoomInButton.toolTipText=Zoom In +VisualizationPanel.zoomInButton.toolTipText=Zoom visualization in. VisualizationPanel.zoomInButton.text= -VisualizationPanel.zoomOutButton.toolTipText=Zoom Out +VisualizationPanel.zoomOutButton.toolTipText=Zoom visualization out. VisualizationPanel.zoomOutButton.text= VisualizationPanel.fastOrganicLayoutButton.text= VisualizationPanel.backButton.text_1= @@ -38,14 +38,14 @@ VisualizationPanel.hierarchyLayoutButton.text=Hierarchical VisualizationPanel.clearVizButton.text_1= VisualizationPanel.snapshotButton.text_1=Snapshot Report VisualizationPanel.clearVizButton.actionCommand= -VisualizationPanel.backButton.toolTipText=Click to Go Back -VisualizationPanel.forwardButton.toolTipText=Click to Go Forward -VisualizationPanel.fastOrganicLayoutButton.toolTipText=Click to Redraw Chart -VisualizationPanel.clearVizButton.toolTipText=Click to Clear Chart +VisualizationPanel.backButton.toolTipText=Click to go back to previous state. +VisualizationPanel.forwardButton.toolTipText=Click to move state forward. +VisualizationPanel.fastOrganicLayoutButton.toolTipText=Click to redraw visualization. +VisualizationPanel.clearVizButton.toolTipText=Click to clear visualization. FiltersPanel.limitHeaderLabel.text=Communications Limit: FiltersPanel.mostRecentLabel.text=Most Recent: FiltersPanel.limitErrorMsgLabel.text=Invalid integer value. VisualizationPanel.forwardButton.text= VisualizationPanel.zoomPercentLabel.text=100% VisualizationPanel.zoomLabel.text=Zoom: -VisualizationPanel.snapshotButton.toolTipText=Generate Snapshot Report +VisualizationPanel.snapshotButton.toolTipText=Generate Snapshot report. From 63b0229123713c8e72634c605e7718f23c81fb77 Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 15 Jul 2019 15:12:55 -0400 Subject: [PATCH 49/69] Updates to picking users documentation. --- .../images/grantAccessToComputer.PNG | Bin 0 -> 5247 bytes .../images/objectTypesComputers.PNG | Bin 0 -> 7500 bytes .../images/sharedStoragePermissions.PNG | Bin 0 -> 9235 bytes docs/doxygen-user/multi-user/installUsers.dox | 18 +++++++++++++----- 4 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 docs/doxygen-user/images/grantAccessToComputer.PNG create mode 100644 docs/doxygen-user/images/objectTypesComputers.PNG create mode 100644 docs/doxygen-user/images/sharedStoragePermissions.PNG diff --git a/docs/doxygen-user/images/grantAccessToComputer.PNG b/docs/doxygen-user/images/grantAccessToComputer.PNG new file mode 100644 index 0000000000000000000000000000000000000000..a155a20ea758306612915278b207d87e1dd2ec45 GIT binary patch literal 5247 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 literal 0 HcmV?d00001 diff --git a/docs/doxygen-user/images/sharedStoragePermissions.PNG b/docs/doxygen-user/images/sharedStoragePermissions.PNG new file mode 100644 index 0000000000000000000000000000000000000000..bea1aa0fd451fecf3442640adebd5504d88047c1 GIT binary patch literal 9235 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 Accounts: If the cluster is on a Windows domain, then Autopsy can be run with a domain account. - If your shared storage is a Windows file share, then users should be able to access it without needing a password. - If your shared storage is another platform, then you'll likely need to force Windows to store the shared storage credentials (as described below). -- Unique Local Unique Accounts: Some clusters are not on a Windows domain and have unique accounts for each analyst / user. +- Unique Local Accounts: Some clusters are not on a Windows domain and have unique accounts for each analyst / user. - If your shared storage is a Windows file share, then users will not need to enter their password IF the same user name and password exists on the file share server. - Otherwise, you'll need to force Windows to store the credentials. - Shared Local Account: Lastly, some clusters use a single local account, such as one named "autopsy" for all users. This is not recommended because Autopsy uses the login name to track who did certain actions, such as tagging files. @@ -32,13 +32,21 @@ The user account that Autopsy runs as will need access to the shared storage. Th Solr will run as a Windows service and will need access to the shared storage. The default user, which is "LocalService", will not have access to network-based storage. So, if you have network-based shared storage, you have three options: -- NetworkService: If you are on a domain, you maybe able to run Solr as the "NetworkService" account. This account has access to the network, but the challenge can be granting access for this account to the shared storage. - - If your shared storage is a Windows file share, you'll need to grant access to the computer running Solr. +- NetworkService: If you are on a domain, you may be able to run Solr as the "NetworkService" account. This account has access to the network, but the challenge can be granting access for this account to the shared storage. + - If your shared storage is a Windows file share, you'll need to grant access to the computer account running Solr as follows: +
    1. Right click on the shared storage folder choose "Properties" and select the "Security" tab. +
    2. Click the "Edit..." button and then click the "Add..." button. +
    3. Click on the "Object Types" button and confirm that the "Computers" object type has a check mark. + \image html objectTypesComputers.PNG +
    4. Enter the name of the computer and click the "Check Names" button to confirm that it is correct. + \image html grantAccessToComputer.PNG +
    5. Ensure that the computer account has both read and write access to shared storage. + \image html sharedStoragePermissions.PNG
    - For other shared storage, you may not be able to access the data from the NetworkService account. -- Normal User: If you are not on a domain or cannot grant access to the computer for share storage, then run Solr as a normal user (local or domain). +- Normal User: If you are not on a domain or cannot grant access to the computer for shared storage, then run Solr as a normal user (local or domain). - If you do this, refer to the scenarios as outlined above for picking an Autopsy user. The same rules will apply with respect to passwords and saving credentials. - The main downside of this is that the service needs to be updated when the account password changes and it may require letting others know about the password. -- LocalService: Lastly, if you using the same server for both Solr and shared storage, then it is possible to run Solr as the default "LocalService" because it doesn't need network access. +- LocalService: Lastly, if you are using the same server for both Solr and shared storage, then it is possible to run Solr as the default "LocalService" because it doesn't need network access. From 978d7ae3580e811ac6a8664282827dac74bc84d5 Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 15 Jul 2019 15:53:32 -0400 Subject: [PATCH 50/69] Fixed typo and added caveat about auto ingest running as a service --- docs/doxygen-user/auto_ingest.dox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/doxygen-user/auto_ingest.dox b/docs/doxygen-user/auto_ingest.dox index 9674249f6f..59e422c666 100644 --- a/docs/doxygen-user/auto_ingest.dox +++ b/docs/doxygen-user/auto_ingest.dox @@ -71,7 +71,7 @@ When auto ingest mode is enabled, Autopsy will open with a different UI than nor \image html AutoIngest/auto_ingest_in_progress.png -The user must press the "Start" button to being the auto ingest process. Note that if the computer running Autopsy in auto ingest mode is restarted, someone must log into it to restart Autopsy. It does not start by itself. When "Start" is pressed, the node will scan through the Shared Images folder looking for manifest files. This scan happens periodically when ingest is running. It can also be started manually using the "Refresh" button. +The user must press the "Start" button to begin the auto ingest process. Note that if the computer running Autopsy in auto ingest mode is restarted and the auto ingest node is not running as a service, someone must log into it to restart Autopsy. It does not start by itself. When "Start" is pressed, the node will scan through the Shared Images folder looking for manifest files. This scan happens periodically when ingest is running. It can also be started manually using the "Refresh" button. The UI for the auto ingest node will display what images are scheduled for analysis, what is currently running, and what has been completed. If a newly added image should be the highest priority, then you can select it and choose "Prioritize Case". This will prioritize all images within the same case to be top priority. You may also prioritize only a single data source (job) using the "Prioritize Job" button in the same manner. If you have prioritized something by mistake, the "Deprioritize" buttons will undo it. From d31719b217bc63c326a52785deb4127f12b6641b Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 15 Jul 2019 15:58:08 -0400 Subject: [PATCH 51/69] addressed review comments --- .../BlackboardArtifactDateComparator.java | 103 +++++++++--------- .../relationships/Bundle.properties-MERGED | 2 +- .../relationships/CallLogNode.java | 25 ++++- .../relationships/CallLogViewer.java | 2 +- .../RelationshipsNodeUtilities.java | 24 +++- 5 files changed, 92 insertions(+), 64 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/BlackboardArtifactDateComparator.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/BlackboardArtifactDateComparator.java index cef2ddd3d9..b4279c8193 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/BlackboardArtifactDateComparator.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/BlackboardArtifactDateComparator.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obt ain 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.communications.relationships; @@ -32,62 +45,14 @@ class BlackboardArtifactDateComparator implements Comparator @Override public int compare(BlackboardArtifact bba1, BlackboardArtifact bba2) { - BlackboardAttribute attribute1 = null; - BlackboardAttribute attribute2 = null; + BlackboardAttribute attribute1 = getTimeAttributeForArtifact(bba1); + BlackboardAttribute attribute2 = getTimeAttributeForArtifact(bba2); // Inializing to Long.MAX_VALUE so that if a BlackboardArtifact of // any unexpected type is passed in, it will bubble to the top of // the list. long dateTime1 = Long.MAX_VALUE; long dateTime2 = Long.MAX_VALUE; - if (bba1 != null) { - BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba1.getArtifactTypeID()); - if (fromID != null) { - try { - switch (fromID) { - case TSK_EMAIL_MSG: - attribute1 = bba1.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT)); - break; - case TSK_MESSAGE: - attribute1 = bba1.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME)); - break; - case TSK_CALLLOG: - attribute1 = bba1.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START)); - break; - default: - attribute1 = null; - break; - } - } catch (TskCoreException ex) { - logger.log(Level.WARNING, String.format("Unable to compare attributes for artifact %d", bba1.getArtifactID()), ex); - } - } - } - - if (bba2 != null) { - BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba2.getArtifactTypeID()); - if (fromID != null) { - try { - switch (fromID) { - case TSK_EMAIL_MSG: - attribute2 = bba2.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT)); - break; - case TSK_MESSAGE: - attribute2 = bba2.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME)); - break; - case TSK_CALLLOG: - attribute2 = bba2.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START)); - break; - default: - attribute2 = null; - break; - } - } catch (TskCoreException ex) { - logger.log(Level.WARNING, String.format("Unable to compare attributes for artifact %d", bba2.getArtifactID()), ex); - } - } - } - if (attribute1 != null) { dateTime1 = attribute1.getValueLong(); } @@ -98,4 +63,36 @@ class BlackboardArtifactDateComparator implements Comparator return Long.compare(dateTime1, dateTime2) * direction; } + + private BlackboardAttribute getTimeAttributeForArtifact(BlackboardArtifact artifact) { + if(artifact == null) { + return null; + } + + BlackboardAttribute attribute = null; + + BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); + if (fromID != null) { + try { + switch (fromID) { + case TSK_EMAIL_MSG: + attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT)); + break; + case TSK_MESSAGE: + attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME)); + break; + case TSK_CALLLOG: + attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START)); + break; + default: + attribute = null; + break; + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to compare attributes for artifact %d", artifact.getArtifactID()), ex); + } + } + + return attribute; + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED index 58eafd7bff..f2aa0df8e1 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED @@ -1,7 +1,7 @@ CallLogViewer_device_label=Device CallLogViewer_duration_label=Duration(seconds) CallLogViewer_noCallLogs= -CallLogViewer_recipient_label=Recipient +CallLogViewer_recipient_label=To/From CallLogViewer_title=Call Logs ContactDetailsPane.nameLabel.text=Placeholder ContactNode_Email=Email Address diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java index 508421c58a..a9e92d6451 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java @@ -1,13 +1,28 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.sleuthkit.autopsy.communications.relationships; +import java.util.logging.Level; import org.openide.nodes.Sheet; import org.sleuthkit.autopsy.communications.Utils; import static org.sleuthkit.autopsy.communications.relationships.RelationshipsNodeUtilities.getAttributeDisplayString; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.datamodel.Account; @@ -27,6 +42,8 @@ import org.sleuthkit.datamodel.TskCoreException; */ final class CallLogNode extends BlackboardArtifactNode { + private static final Logger logger = Logger.getLogger(CallLogNode.class.getName()); + final static String DURATION_PROP = "duration"; CallLogNode(BlackboardArtifact artifact, String deviceID) { @@ -59,7 +76,7 @@ final class CallLogNode extends BlackboardArtifactNode { try{ duration = getCallDuration(artifact); } catch(TskCoreException ex) { - + logger.log(Level.WARNING, String.format("Unable to get calllog duration for artifact: %d", artifact.getArtifactID()), ex); } sheetSet.put(createNode(TSK_DATETIME_START, artifact)); diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.java index 344335aeea..cb5d34d7f9 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogViewer.java @@ -44,7 +44,7 @@ final class CallLogViewer extends javax.swing.JPanel implements RelationshipsVie @Messages({ "CallLogViewer_title=Call Logs", "CallLogViewer_noCallLogs=", - "CallLogViewer_recipient_label=Recipient", + "CallLogViewer_recipient_label=To/From", "CallLogViewer_duration_label=Duration(seconds)", "CallLogViewer_device_label=Device" }) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java index fd2509aba9..361eb1d7ad 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java @@ -1,8 +1,22 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package org.sleuthkit.autopsy.communications.relationships; import java.util.TimeZone; @@ -16,10 +30,10 @@ import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TskCoreException; /** - * + * A set of reusable utility functions for the Relationships package. * */ -public class RelationshipsNodeUtilities { +final class RelationshipsNodeUtilities { private static final Logger logger = Logger.getLogger(RelationshipsNodeUtilities.class.getName()); /** From 6b8e0a13d51e7c03a3b111128b84d5e4ea785327 Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 15 Jul 2019 16:17:23 -0400 Subject: [PATCH 52/69] Updates to PostgreSQL installation procedures. --- docs/doxygen-user/multi-user/installPostgres.dox | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/doxygen-user/multi-user/installPostgres.dox b/docs/doxygen-user/multi-user/installPostgres.dox index 0164a1fd1a..fd7753281b 100644 --- a/docs/doxygen-user/multi-user/installPostgres.dox +++ b/docs/doxygen-user/multi-user/installPostgres.dox @@ -5,7 +5,7 @@ In a multi-user case, a central PostgreSQL database server is used instead of the embedded SQLite databases. -A new database will be created for each case and the database will be stored on the local drive. +A new database will be created for each case and the database will be stored in a location you choose during installation. It is recommended that you choose a drive that is local to the machine and is not the system drive. You should ensure that the database folder is backed up. @@ -17,7 +17,7 @@ To install PostgreSQL, perform the following steps: 2. Run the installer. The name will be similar to _postgresql-9.5.3-1-windows-x64.exe_. -3. You may accept defaults for all items except for the password as you work through the wizard. Do not lose the password you enter in. This is the PostgreSQL administrator login password. +3. You may accept defaults for all items except for the password and the database storage location as you work through the wizard. Do not lose the password you enter in. This is the PostgreSQL administrator login password. 4. You do not need to launch the StackBuilder nor acquire any more software from it. Uncheck the option to use StackBuilder and press _Finish_. @@ -103,17 +103,17 @@ Note the removal of the leading number symbol-this uncomments that entry. You can verify that PostgreSQL is running by using either the _pgAdmin_ tool or the _psql_ tool to connect to the database server from another machine on the network. -Common problems are from: -- Firewall blocking the ports on the PostgreSQL server -- Incorrect credentials -- Wrong IP address range in the configuration file +Common problems are typically the result of: +- Firewall blocking the port (default: 5432) on the PostgreSQL server. +- Incorrectly configured database user account or incorrect credentials. +- Incorrectly configured IP address range in pg_hba.conf file. \section install_post_backup Backing Up -The database are stored on the local drive (not shared storage). So, you should backup that directory periodically. +The databases and configuration files are stored at the location you chose during PostgreSQL installation (not shared storage). So, you should backup that directory periodically. -The default location is XYZ. +For an installation where the default options were chosen, the directory can be found at C:\\Program Files\\PostgreSQL\\9.5\\data. */ From 3ff00bb5682d7b0028ddd7f6b3a687050e8178b2 Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 15 Jul 2019 16:31:12 -0400 Subject: [PATCH 53/69] Added the option of having AV software ignore case output folder. --- docs/doxygen-user/installation.dox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/doxygen-user/installation.dox b/docs/doxygen-user/installation.dox index 57e4f59a2f..1577806f54 100644 --- a/docs/doxygen-user/installation.dox +++ b/docs/doxygen-user/installation.dox @@ -23,7 +23,7 @@ We distribute a Windows installer and ZIP files to run on Linux and OS X. \subsection prereqs_av AntiVirus -You should consider disabling AntiVirus software that runs on your computers that will be running Autopsy. AntiVirus software may quarantine or even delete some of your results before you get a chance to look at them. Autopsy encodes some of the files that it extracts so that they can not be executed or scanned, but some modules (such as our ZIP extraction module) will directly write files to disk in an unencoded format. +You should consider either disabling AntiVirus software that runs on your computers that will be running Autopsy or configuring your AntiVirus software to ignore the contents of your case output directory. AntiVirus software may quarantine or even delete some of your results before you get a chance to look at them. Autopsy encodes some of the files that it extracts so that they can not be executed or scanned, but some modules (such as our ZIP extraction module) will directly write files to disk in an unencoded format. Of course, the risk with disabling AntiVirus software is that your computer could be infected with malware that came from your media. From ef188cbe16b255b77b050ef753830903ce1a9a7a Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 15 Jul 2019 17:13:41 -0400 Subject: [PATCH 54/69] Updated ActiveMQ test procedures. --- docs/doxygen-user/multi-user/installActiveMQ.dox | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/doxygen-user/multi-user/installActiveMQ.dox b/docs/doxygen-user/multi-user/installActiveMQ.dox index 8a0d9405a2..4d7ab37fe5 100644 --- a/docs/doxygen-user/multi-user/installActiveMQ.dox +++ b/docs/doxygen-user/multi-user/installActiveMQ.dox @@ -45,14 +45,16 @@ To test your installation, you can access the admin pages in your web browser vi The default administrator username is _admin_ with a password of _admin_ and the default regular username is _user_ with a default password of _password_. You can change these passwords by following the instructions below. -If you can see a page that looks like the following, the service is ready to function. +If you can see a page that looks like the following, it confirms that the ActiveMQ service is running locally but it does not necessarily mean that the service is visible to other computers on the network.

    \image html activemq.PNG

    -If you do not see this page: +You can confirm that your ActiveMQ installation is visible to other computers on the network by attempting to connect to a URL like the following (replacing the host name with that of the ActiveMQ computer) in a web browser: http://activemq-computer:61616 + +If you are unable to connect to this address: - Double check that the ActiveMQ service is running -- Check that the port is not being blocked by a firewall. +- Check that the port (61616) is not being blocked by a firewall. From a02f02b700748c0dfd72cabdcdbedeaab43a6d78 Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 15 Jul 2019 17:38:46 -0400 Subject: [PATCH 55/69] Removed reference to running as a service. --- docs/doxygen-user/auto_ingest.dox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/doxygen-user/auto_ingest.dox b/docs/doxygen-user/auto_ingest.dox index 59e422c666..3957e7c922 100644 --- a/docs/doxygen-user/auto_ingest.dox +++ b/docs/doxygen-user/auto_ingest.dox @@ -71,7 +71,7 @@ When auto ingest mode is enabled, Autopsy will open with a different UI than nor \image html AutoIngest/auto_ingest_in_progress.png -The user must press the "Start" button to begin the auto ingest process. Note that if the computer running Autopsy in auto ingest mode is restarted and the auto ingest node is not running as a service, someone must log into it to restart Autopsy. It does not start by itself. When "Start" is pressed, the node will scan through the Shared Images folder looking for manifest files. This scan happens periodically when ingest is running. It can also be started manually using the "Refresh" button. +The user must press the "Start" button to begin the auto ingest process. Note that if the computer running Autopsy in auto ingest mode is restarted, someone must log into it to restart Autopsy. It does not start by itself. When "Start" is pressed, the node will scan through the Shared Images folder looking for manifest files. This scan happens periodically when ingest is running. It can also be started manually using the "Refresh" button. The UI for the auto ingest node will display what images are scheduled for analysis, what is currently running, and what has been completed. If a newly added image should be the highest priority, then you can select it and choose "Prioritize Case". This will prioritize all images within the same case to be top priority. You may also prioritize only a single data source (job) using the "Prioritize Job" button in the same manner. If you have prioritized something by mistake, the "Deprioritize" buttons will undo it. From 1ff87cf38a1475abd00c029f4645d519219f1360 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Tue, 16 Jul 2019 08:38:37 -0400 Subject: [PATCH 56/69] Add more chunking options --- .../org/sleuthkit/autopsy/report/PortableCaseReportModule.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/report/PortableCaseReportModule.java b/Core/src/org/sleuthkit/autopsy/report/PortableCaseReportModule.java index ae2fc88766..eab436771b 100644 --- a/Core/src/org/sleuthkit/autopsy/report/PortableCaseReportModule.java +++ b/Core/src/org/sleuthkit/autopsy/report/PortableCaseReportModule.java @@ -983,6 +983,9 @@ class PortableCaseReportModule implements ReportModule { enum ChunkSize { NONE("Do not split", ""), // NON-NLS + ONE_HUNDRED_MB("Split into 100 MB chunks", "100m"), + CD("Split into 700 MB chunks (CD)", "700m"), + ONE_GB("Split into 1 GB chunks", "1000m"), DVD("Split into 4.5 GB chunks (DVD)", "4500m"); // NON-NLS private final String displayName; From 26c8a984ab16fae311448be050dbb81a1b9adce4 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Tue, 16 Jul 2019 11:50:52 -0400 Subject: [PATCH 57/69] Add new methods to subscribe to specified ingest events. --- .../autopsy/ingest/IngestManager.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java index b59e0a6ccb..e345db3d65 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java @@ -491,6 +491,18 @@ public class IngestManager implements IngestProgressSnapshotProvider { public void addIngestJobEventListener(final PropertyChangeListener listener) { jobEventPublisher.addSubscriber(INGEST_JOB_EVENT_NAMES, listener); } + + /** + * Adds an ingest job event property change listener for the given event types. + * + * @param eventTypes The event types to listen for + * @param listener The PropertyChangeListener to be added + */ + public void addIngestJobEventListener(Set eventTypes, final PropertyChangeListener listener) { + eventTypes.forEach((IngestJobEvent event) -> { + jobEventPublisher.addSubscriber(event.toString(), listener); + }); + } /** * Removes an ingest job event property change listener. @@ -500,6 +512,18 @@ public class IngestManager implements IngestProgressSnapshotProvider { public void removeIngestJobEventListener(final PropertyChangeListener listener) { jobEventPublisher.removeSubscriber(INGEST_JOB_EVENT_NAMES, listener); } + + /** + * Removes an ingest job event property change listener. + * + * @param eventTypes The event types to stop listening for + * @param listener The PropertyChangeListener to be removed. + */ + public void removeIngestJobEventListener(Set eventTypes, final PropertyChangeListener listener) { + eventTypes.forEach((IngestJobEvent event) -> { + jobEventPublisher.removeSubscriber(event.toString(), listener); + }); + } /** * Adds an ingest module event property change listener. @@ -510,6 +534,18 @@ public class IngestManager implements IngestProgressSnapshotProvider { moduleEventPublisher.addSubscriber(INGEST_MODULE_EVENT_NAMES, listener); } + /** + * Adds an ingest module event property change listener for given event types. + * + * @param eventTypes The event types to listen for + * @param listener The PropertyChangeListener to be removed. + */ + public void addIngestModuleEventListener(Set eventTypes, final PropertyChangeListener listener) { + eventTypes.forEach((IngestModuleEvent event) -> { + moduleEventPublisher.addSubscriber(event.toString(), listener); + }); + } + /** * Removes an ingest module event property change listener. * @@ -518,6 +554,16 @@ public class IngestManager implements IngestProgressSnapshotProvider { public void removeIngestModuleEventListener(final PropertyChangeListener listener) { moduleEventPublisher.removeSubscriber(INGEST_MODULE_EVENT_NAMES, listener); } + + /** + * Removes an ingest module event property change listener. + * + * @param eventTypes The event types to stop listening for + * @param listener The PropertyChangeListener to be removed. + */ + public void removeIngestModuleEventListener(Set eventTypes, final PropertyChangeListener listener) { + moduleEventPublisher.removeSubscriber(INGEST_MODULE_EVENT_NAMES, listener); + } /** * Publishes an ingest job event signifying an ingest job started. From b0548c725d06e1c9a04e3b8c84e49940906c27ce Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Tue, 16 Jul 2019 11:55:15 -0400 Subject: [PATCH 58/69] removed unused import --- .../autopsy/communications/relationships/ContactDetailsPane.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java index beec25a0e0..d8592a0f1d 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java @@ -26,7 +26,6 @@ import javax.imageio.ImageIO; import javax.swing.ImageIcon; import org.openide.explorer.ExplorerManager; import org.openide.nodes.Node; -import org.openide.util.Exceptions; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; From 7adc8a6a2afa418484f1d35b07b5d1b11e0fd3c1 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Tue, 16 Jul 2019 12:33:37 -0400 Subject: [PATCH 59/69] addressed codacy issues --- .../CallLogsChildNodeFactory.java | 36 +++++++++++++++++-- .../relationships/RelationshipBrowser.java | 18 ++++------ .../RelationshipsNodeUtilities.java | 5 +++ 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java index 427b6ff1e5..5bfd1bdb96 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogsChildNodeFactory.java @@ -48,7 +48,7 @@ final class CallLogsChildNodeFactory extends ChildFactory{ private SelectionInfo selectionInfo; - private Map deviceIDMap = new HashMap(); + private final Map deviceIDMap = new HashMap<>(); CallLogsChildNodeFactory(SelectionInfo selectionInfo) { this.selectionInfo = selectionInfo; @@ -107,6 +107,19 @@ final class CallLogsChildNodeFactory extends ChildFactory{ return new CallLogNode(key.getArtifact(), key.getDeviceID()); } + /** + * Gets the device ID for the given data source. + * + * To reduce lookup calls to the DB unique dataSourceName\deviceID pairs + * are stored in deviceIDMap. + * + * @param dataSourceName String name of data source + * + * @return device ID for given dataSourceName or empty string if non is found. + * + * @throws NoCurrentCaseException + * @throws TskCoreException + */ private String getDeviceIDForDataSource(String dataSourceName) throws NoCurrentCaseException, TskCoreException{ String deviceID = deviceIDMap.get(dataSourceName); @@ -128,7 +141,7 @@ final class CallLogsChildNodeFactory extends ChildFactory{ // This list should just have 1 item in it List adiList = manager.getAccountDeviceInstancesWithRelationships(filter); - if(adiList != null && adiList.size() > 0) { + if( adiList != null && !adiList.isEmpty() ) { deviceID = adiList.get(0).getDeviceId(); } else { deviceID = ""; @@ -137,9 +150,13 @@ final class CallLogsChildNodeFactory extends ChildFactory{ deviceIDMap.put(dataSourceName, deviceID); } - return deviceID; + return (deviceID != null ? deviceID : ""); } + /** + * ChildFactory key class which contains a BlackboardArtifact and its + * data source deviceID + */ final class CallLogNodeKey{ private final BlackboardArtifact artifact; private final String deviceID; @@ -149,15 +166,28 @@ final class CallLogsChildNodeFactory extends ChildFactory{ this.deviceID = deviceID; } + /** + * Get the BlackboardArtifact for this key + * + * @return BlackboardArtifact instance + */ BlackboardArtifact getArtifact() { return artifact; } + /** + * Gets the BlackboardArtifact data source device ID. + * + * @return String device id. + */ String getDeviceID() { return deviceID; } } + /** + * A comparator for CallLogNodeKey objects + */ final class CallLogComparator implements Comparator{ final BlackboardArtifactDateComparator comparator; diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.java index 9f6309d6af..f1ef86d244 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.java @@ -30,13 +30,6 @@ import org.sleuthkit.autopsy.communications.ModifiableProxyLookup; public final class RelationshipBrowser extends JPanel implements Lookup.Provider { private SelectionInfo currentSelection; - - private final MessageViewer messagesViewer; - private final ContactsViewer contactsViewer; - private final SummaryViewer summaryViewer; - private final MediaViewer mediaViewer; - private final CallLogViewer callLogViewer; - private final ModifiableProxyLookup proxyLookup; /** @@ -44,11 +37,12 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider */ public RelationshipBrowser() { initComponents(); - messagesViewer = new MessageViewer(); - contactsViewer = new ContactsViewer(); - summaryViewer = new SummaryViewer(); - mediaViewer = new MediaViewer(); - callLogViewer = new CallLogViewer(); + + MessageViewer messagesViewer = new MessageViewer(); + ContactsViewer contactsViewer = new ContactsViewer(); + SummaryViewer summaryViewer = new SummaryViewer(); + MediaViewer mediaViewer = new MediaViewer(); + CallLogViewer callLogViewer = new CallLogViewer(); proxyLookup = new ModifiableProxyLookup(messagesViewer.getLookup()); diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java index 361eb1d7ad..db2f6f6ebd 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java @@ -36,6 +36,11 @@ import org.sleuthkit.datamodel.TskCoreException; final class RelationshipsNodeUtilities { private static final Logger logger = Logger.getLogger(RelationshipsNodeUtilities.class.getName()); + + // Here to make codacy happy + private RelationshipsNodeUtilities(){ + } + /** * * Get the display string for the attribute of the given type from the given From 9129852150e19bfffa32f705313f6afe97638a0a Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Tue, 16 Jul 2019 13:15:09 -0400 Subject: [PATCH 60/69] addressed codacy issues --- .../autopsy/thunderbirdparser/MboxParser.java | 12 +++---- .../autopsy/thunderbirdparser/PstParser.java | 30 +++++++++--------- .../ThunderbirdMboxFileIngestModule.java | 31 ++----------------- 3 files changed, 23 insertions(+), 50 deletions(-) diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java index 7c3d74121b..fca292963e 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java @@ -68,7 +68,7 @@ class MboxParser implements Iterator { private static final Logger logger = Logger.getLogger(MboxParser.class.getName()); private final DefaultMessageBuilder messageBuilder; - private final StringBuilder errors; + private String errors; /** * The mime type string for html text. @@ -88,7 +88,7 @@ class MboxParser implements Iterator { MimeConfig config = MimeConfig.custom().setMaxLineLen(-1).build(); // disable line length checks. messageBuilder.setMimeEntityConfig(config); - errors = new StringBuilder(); + errors = new String(); } static boolean isValidMimeTypeMbox(byte[] buffer) { @@ -162,7 +162,7 @@ class MboxParser implements Iterator { @Override public boolean hasNext() { - return emailIterator != null ? emailIterator.hasNext() : false; + return emailIterator != null && emailIterator.hasNext(); } @Override @@ -171,7 +171,7 @@ class MboxParser implements Iterator { } String getErrors() { - return errors.toString(); + return errors; } /** @@ -499,7 +499,7 @@ class MboxParser implements Iterator { } private void addErrorMessage(String msg) { - errors.append("
  • ").append(msg).append("
  • "); //NON-NLS + errors = errors + "
  • " + msg + "
  • "; //NON-NLS } /** @@ -521,7 +521,7 @@ class MboxParser implements Iterator { @Override public boolean hasNext() { - return (mboxIterator != null && encoder != null) ? mboxIterator.hasNext() : false; + return (mboxIterator != null && encoder != null) && mboxIterator.hasNext(); } @Override diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java index d8a8932536..3fa2dc5884 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java @@ -61,7 +61,7 @@ class PstParser { * A map of PSTMessages to their Local path within the file's internal * directory structure. */ - private final StringBuilder errors; + private String errors; private final IngestServices services; @@ -71,7 +71,7 @@ class PstParser { private int failureCount = 0; PstParser(IngestServices services) { - errors = new StringBuilder(); + errors = new String(); this.services = services; } @@ -174,7 +174,7 @@ class PstParser { * @return String error list, empty string if no errors exist. */ String getErrors() { - return errors.toString(); + return errors; } /** @@ -480,7 +480,7 @@ class PstParser { * @param msg String message to add */ private void addErrorMessage(String msg) { - errors.append("
  • ").append(msg).append("
  • "); //NON-NLS + errors = errors + "
  • " + msg + "
  • "; //NON-NLS } /** @@ -522,8 +522,8 @@ class PstParser { private final class PstEmailIterator implements Iterator { private final PSTFolder folder; - private EmailMessage current; - private EmailMessage next; + private EmailMessage currentMsg; + private EmailMessage nextMsg; private final String currentPath; private final long fileID; @@ -548,9 +548,9 @@ class PstParser { PSTMessage message = (PSTMessage) folder.getNextChild(); if (message != null) { if (wholeMsg) { - next = extractEmailMessage(message, currentPath, fileID); + nextMsg = extractEmailMessage(message, currentPath, fileID); } else { - next = extractPartialEmailMessage(message); + nextMsg = extractPartialEmailMessage(message); } } } catch (PSTException | IOException ex) { @@ -562,32 +562,32 @@ class PstParser { @Override public boolean hasNext() { - return next != null; + return nextMsg != null; } @Override public EmailMessage next() { - current = next; + currentMsg = nextMsg; try { PSTMessage message = (PSTMessage) folder.getNextChild(); if (message != null) { if (wholeMsg) { - next = extractEmailMessage(message, currentPath, fileID); + nextMsg = extractEmailMessage(message, currentPath, fileID); } else { - next = extractPartialEmailMessage(message); + nextMsg = extractPartialEmailMessage(message); } } else { - next = null; + nextMsg = null; } } catch (PSTException | IOException ex) { logger.log(Level.WARNING, String.format("Unable to extract emails for path: %s file ID: %d ", currentPath, fileID), ex); failureCount++; - next = null; + nextMsg = null; } - return current; + return currentMsg; } /** diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java index b91841aa94..ce25c83bc7 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java @@ -408,37 +408,10 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { * Take the extracted information in the email messages and add the * appropriate artifacts and derived files. * - * @param emails + * @param partialEmailsForThreading + * @param fileMessageIterator * @param abstractFile */ - private void processEmails(List emails, AbstractFile abstractFile) { - List derivedFiles = new ArrayList<>(); - - // Putting try/catch around this to catch any exception and still allow - // the creation of the artifacts to continue. - try{ - EmailMessageThreader.threadMessages(emails, String.format("%d", abstractFile.getId())); - } catch(Exception ex) { - logger.log(Level.WARNING, String.format("Exception thrown parsing emails from %s", abstractFile.getName()), ex); - } - - for (EmailMessage email : emails) { - BlackboardArtifact msgArtifact = addEmailArtifact(email, abstractFile); - - if ((msgArtifact != null) && (email.hasAttachment())) { - derivedFiles.addAll(handleAttachments(email.getAttachments(), abstractFile, msgArtifact )); - } - } - - if (derivedFiles.isEmpty() == false) { - for (AbstractFile derived : derivedFiles) { - services.fireModuleContentEvent(new ModuleContentEvent(derived)); - } - } - context.addFilesToJob(derivedFiles); - services.fireModuleDataEvent(new ModuleDataEvent(EmailParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG)); - } - private void processEmails(List partialEmailsForThreading, Iterator fullMessageIterator, AbstractFile abstractFile) { // Putting try/catch around this to catch any exception and still allow // the creation of the artifacts to continue. From b8f4d1690812514d36f02b9cf61443495ac2237e Mon Sep 17 00:00:00 2001 From: esaunders Date: Tue, 16 Jul 2019 15:05:26 -0400 Subject: [PATCH 61/69] Attempt to communicate with local Solr server after launch, waiting up to 30 seconds on startup to give it time to initialize. --- .../keywordsearch/Bundle.properties-MERGED | 3 +- .../autopsy/keywordsearch/Server.java | 30 ++++++++++++++----- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED index 2ba6856d7e..f5e139fbd3 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED @@ -36,7 +36,7 @@ KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found KeywordSearchResultFactory.query.exception.msg=Could not perform the query OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\n\The module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found. +OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\nThe module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found. OpenIDE-Module-Name=KeywordSearch OptionsCategory_Name_KeywordSearchOptions=Keyword Search OptionsCategory_Keywords_KeywordSearchOptions=Keyword Search @@ -303,6 +303,7 @@ KeywordSearchModuleFactory.createFileIngestModule.exception.msg=Expected setting SearchRunner.Searcher.done.err.msg=Error performing keyword search KeywordSearchGlobalSearchSettingsPanel.timeRadioButton5.toolTipText=Fastest overall, but no results until the end KeywordSearchGlobalSearchSettingsPanel.timeRadioButton5.text=No periodic searches +Server.status.failed.msg=Local Solr server did not respond to status request. This may be because the server failed to start or is taking too long to initialize. SolrConnectionCheck.HostnameOrPort=Invalid hostname and/or port number. SolrConnectionCheck.Hostname=Invalid hostname. SolrConnectionCheck.MissingHostname=Missing hostname. diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index 0234ac7f73..7259f29966 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -42,6 +42,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Random; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Level; import javax.swing.AbstractAction; @@ -428,6 +429,8 @@ public class Server { * immediately (probably before the server is ready) and doesn't check * whether it was successful. */ + @NbBundle.Messages({ + "Server.status.failed.msg=Local Solr server did not respond to status request. This may be because the server failed to start or is taking too long to initialize.",}) void start() throws KeywordSearchModuleException, SolrServerNoPortException { if (isRunning()) { // If a Solr server is running we stop it. @@ -469,16 +472,27 @@ public class Server { Arrays.asList("-Dbootstrap_confdir=../solr/configsets/AutopsyConfig/conf", //NON-NLS "-Dcollection.configName=AutopsyConfig"))); //NON-NLS - try { - //block for 10 seconds, give time to fully start the process - //so if it's restarted solr operations can be resumed seamlessly - Thread.sleep(10 * 1000); - } catch (InterruptedException ex) { - logger.log(Level.WARNING, "Timer interrupted"); //NON-NLS + // Wait for the Solr server to start and respond to a status request. + for (int numRetries = 0; numRetries < 6; numRetries++) { + if (isRunning()) { + final List pids = this.getSolrPIDs(); + logger.log(Level.INFO, "New Solr process PID: {0}", pids); //NON-NLS + return; + } + + // Local Solr server did not respond so we sleep for + // 5 seconds before trying again. + try { + TimeUnit.SECONDS.sleep(5); + } catch (InterruptedException ex) { + logger.log(Level.WARNING, "Timer interrupted"); //NON-NLS + } } - final List pids = this.getSolrPIDs(); - logger.log(Level.INFO, "New Solr process PID: {0}", pids); //NON-NLS + // If we get here the Solr server has not responded to connection + // attempts in a timely fashion. + logger.log(Level.WARNING, "Local Solr server failed to respond to status requests."); + throw new KeywordSearchModuleException(Bundle.Server_status_failed_msg()); } catch (SecurityException ex) { logger.log(Level.SEVERE, "Could not start Solr process!", ex); //NON-NLS throw new KeywordSearchModuleException( From 08054b29c32758913e8e1c52e9b5fff1d8bb7ca9 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 18 Jul 2019 12:34:58 -0400 Subject: [PATCH 62/69] 5057 disable compression check for gzip files --- .../modules/embeddedfileextractor/SevenZipExtractor.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java index 1fb60cf0cb..d8d0f882da 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java @@ -185,7 +185,11 @@ class SevenZipExtractor { //As a result, many corrupted files have wonky compression ratios and could flood the UI //with false zip bomb notifications. The decision was made to skip compression ratio checks //for unallocated zip files. Instead, we let the depth be an indicator of a zip bomb. - if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC)) { + //Gzip archives compress a single file. They may have a sparse file, + //and that file could be much larger, however it won't be the exponential growth seen with more dangerous zip bombs. + //In addition a fair number of browser cache files will be gzip archives, + //and their file sizes are frequently retrieved incorrectly so ignoring gzip files is a reasonable decision. + if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC) || archiveFile.getMIMEType().equalsIgnoreCase(SupportedArchiveExtractionFormats.XGZIP.toString())) { return false; } From b7f3de7abb74c64010321e9a9b180e81caeb676e Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Mon, 22 Jul 2019 15:30:56 -0400 Subject: [PATCH 63/69] Added null safety and support for null relative paths --- .../autopsy/report/ReportGenerator.java | 28 ++++++++++--------- .../autopsy/report/ReportVisualPanel1.java | 5 ++-- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java index f11f5c0a33..45b4182b0e 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-2018 Basis Technology Corp. + * Copyright 2013-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -56,7 +56,8 @@ class ReportGenerator { private static final Logger logger = Logger.getLogger(ReportGenerator.class.getName()); /** - * Progress reportGenerationPanel that can be used to check for cancellation. + * Progress reportGenerationPanel that can be used to check for + * cancellation. */ private ReportProgressPanel progressPanel; @@ -89,7 +90,6 @@ class ReportGenerator { this.errorList = new ArrayList<>(); } - /** * Display the progress panels to the user, and add actions to close the * parent dialog. @@ -138,9 +138,9 @@ class ReportGenerator { * Run the TableReportModules using a SwingWorker. * * @param artifactTypeSelections the enabled/disabled state of the artifact - * types to be included in the report - * @param tagSelections the enabled/disabled state of the tag names - * to be included in the report + * types to be included in the report + * @param tagSelections the enabled/disabled state of the tag names to be + * included in the report */ void generateTableReport(TableReportModule tableReport, Map artifactTypeSelections, Map tagNameSelections) throws IOException { if (tableReport != null && null != artifactTypeSelections) { @@ -164,7 +164,7 @@ class ReportGenerator { * Run the FileReportModules using a SwingWorker. * * @param enabledInfo the Information that should be included about each - * file in the report. + * file in the report. */ void generateFileListReport(FileReportModule fileReportModule, Map enabledInfo) throws IOException { if (fileReportModule != null && null != enabledInfo) { @@ -219,7 +219,7 @@ class ReportGenerator { displayProgressPanel(); } } - + /** * Run the Portable Case Report Module */ @@ -258,10 +258,12 @@ class ReportGenerator { private void setupProgressPanel(ReportModule module, String reportDir) { String reportFilePath = module.getRelativeFilePath(); - if (!reportFilePath.isEmpty()) { - this.progressPanel = reportGenerationPanel.addReport(module.getName(), reportDir + reportFilePath); - } else { + if (reportFilePath == null) { this.progressPanel = reportGenerationPanel.addReport(module.getName(), null); + } else if (reportFilePath.isEmpty()) { + this.progressPanel = reportGenerationPanel.addReport(module.getName(), reportDir); + } else { + this.progressPanel = reportGenerationPanel.addReport(module.getName(), reportDir + reportFilePath); } } @@ -283,9 +285,9 @@ class ReportGenerator { } catch (IOException ex) { throw new IOException("Failed to make report folder, unable to generate reports.", ex); } - return reportPath; + return reportPath; } - + private class ReportWorker extends SwingWorker { private final Runnable doInBackground; diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel1.java index 8c4c14aff0..2d12230e65 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel1.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012-2018 Basis Technology Corp. + * Copyright 2012-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -147,8 +147,7 @@ final class ReportVisualPanel1 extends JPanel implements ListSelectionListener { // Make sure that the report module has a valid non-null name. private boolean moduleIsValid(ReportModule module) { - return module.getName() != null && !module.getName().isEmpty() - && module.getRelativeFilePath() != null; + return module.getName() != null && !module.getName().isEmpty(); } private void popupWarning(ReportModule module) { From af11e92112aae568093a83a3707021bf117b3cba Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Mon, 22 Jul 2019 16:22:36 -0400 Subject: [PATCH 64/69] Updated doc unmerged other PR --- Core/src/org/sleuthkit/autopsy/report/ReportModule.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportModule.java b/Core/src/org/sleuthkit/autopsy/report/ReportModule.java index 2109d8a526..98b1454fe6 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportModule.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportModule.java @@ -1,4 +1,4 @@ - /* +/* * * Autopsy Forensic Browser * @@ -45,8 +45,9 @@ interface ReportModule { * module. The path should be relative to the location that gets passed in * to generateReport() (or similar). * - * @return Relative path to where report will be stored. May be null if the - * module does not produce a report file. + * @return Relative path to where report will be stored. Return an empty + * string if the location passed to generateReport() is the output location. + * Return null to indicate that there is no report file. */ public String getRelativeFilePath(); @@ -55,7 +56,7 @@ interface ReportModule { * report configuration step of the report wizard. * * @return Configuration panel or null if the module does not need - * configuration. + * configuration. */ public default JPanel getConfigurationPanel() { return new DefaultReportConfigurationPanel(); From 9f1ec6bcd179c1ced8f4704114ee7be73ddc16dd Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Mon, 22 Jul 2019 16:24:04 -0400 Subject: [PATCH 65/69] Updated date in header --- Core/src/org/sleuthkit/autopsy/report/ReportModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportModule.java b/Core/src/org/sleuthkit/autopsy/report/ReportModule.java index 98b1454fe6..8796041474 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportModule.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportModule.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2012-2018 Basis Technology Corp. + * Copyright 2012-2019 Basis Technology Corp. * * Copyright 2012 42six Solutions. * Contact: aebadirad 42six com From 3955136e9d76c788801d995c9a736c364268c208 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 22 Jul 2019 17:31:08 -0400 Subject: [PATCH 66/69] Changed the error string to a list of strings --- .../autopsy/thunderbirdparser/MboxParser.java | 11 +++++++---- .../autopsy/thunderbirdparser/PstParser.java | 16 ++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java index fca292963e..2b1c458c1c 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java @@ -68,7 +68,7 @@ class MboxParser implements Iterator { private static final Logger logger = Logger.getLogger(MboxParser.class.getName()); private final DefaultMessageBuilder messageBuilder; - private String errors; + private List errorList = new ArrayList<>(); /** * The mime type string for html text. @@ -88,7 +88,6 @@ class MboxParser implements Iterator { MimeConfig config = MimeConfig.custom().setMaxLineLen(-1).build(); // disable line length checks. messageBuilder.setMimeEntityConfig(config); - errors = new String(); } static boolean isValidMimeTypeMbox(byte[] buffer) { @@ -171,7 +170,11 @@ class MboxParser implements Iterator { } String getErrors() { - return errors; + String result = ""; + for (String msg: errorList) { + result += "
  • " + msg + "
  • "; + } + return result; } /** @@ -499,7 +502,7 @@ class MboxParser implements Iterator { } private void addErrorMessage(String msg) { - errors = errors + "
  • " + msg + "
  • "; //NON-NLS + errorList.add(msg); } /** diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java index 3fa2dc5884..a8be0a4a93 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java @@ -57,11 +57,6 @@ class PstParser { * First four bytes of a pst file. */ private static int PST_HEADER = 0x2142444E; - /** - * A map of PSTMessages to their Local path within the file's internal - * directory structure. - */ - private String errors; private final IngestServices services; @@ -69,9 +64,10 @@ class PstParser { private long fileID; private int failureCount = 0; + + private List errorList = new ArrayList<>(); PstParser(IngestServices services) { - errors = new String(); this.services = services; } @@ -174,7 +170,11 @@ class PstParser { * @return String error list, empty string if no errors exist. */ String getErrors() { - return errors; + String result = ""; + for (String msg: errorList) { + result += "
  • " + msg + "
  • "; + } + return result; } /** @@ -480,7 +480,7 @@ class PstParser { * @param msg String message to add */ private void addErrorMessage(String msg) { - errors = errors + "
  • " + msg + "
  • "; //NON-NLS + errorList.add(msg); } /** From 7c7513dc2dbd86de3e13d4bcc88ecdfcc0697091 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Tue, 23 Jul 2019 11:53:50 -0400 Subject: [PATCH 67/69] Returned null in tagged hash report module to keep behavior --- .../autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java index fd81e69b10..3c1111bf7a 100644 --- a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java +++ b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java @@ -65,7 +65,7 @@ public class AddTaggedHashesToHashDb implements GeneralReportModule { @Override public String getRelativeFilePath() { - return ""; + return null; } @Messages({ From 51c647b3f002c8121947dde5013d4f2a35006371 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 24 Jul 2019 11:18:02 -0400 Subject: [PATCH 68/69] fixed codacy issues --- .../src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java | 2 +- .../src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java index 2b1c458c1c..5be33551ef 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java @@ -68,7 +68,7 @@ class MboxParser implements Iterator { private static final Logger logger = Logger.getLogger(MboxParser.class.getName()); private final DefaultMessageBuilder messageBuilder; - private List errorList = new ArrayList<>(); + private final List errorList = new ArrayList<>(); /** * The mime type string for html text. diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java index a8be0a4a93..f3b357502f 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java @@ -65,7 +65,7 @@ class PstParser { private int failureCount = 0; - private List errorList = new ArrayList<>(); + private final List errorList = new ArrayList<>(); PstParser(IngestServices services) { this.services = services; From 390cc300b65eb7fda3fcee1554c09f1770793ef7 Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 24 Jul 2019 12:59:49 -0400 Subject: [PATCH 69/69] See if setting jna.nosys to true fixes our AppVeyor build problem. --- Core/src/org/sleuthkit/autopsy/core/Installer.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/core/Installer.java b/Core/src/org/sleuthkit/autopsy/core/Installer.java index 329fe3dfc9..65b5fbacdb 100644 --- a/Core/src/org/sleuthkit/autopsy/core/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/core/Installer.java @@ -299,6 +299,10 @@ public class Installer extends ModuleInstall { * initialized later. */ private static void addGstreamerPathsToEnv() { + if (System.getProperty("jna.nosys") == null) { + System.setProperty("jna.nosys", "true"); + } + Path gstreamerPath = InstalledFileLocator.getDefault().locate("gstreamer", Installer.class.getPackage().getName(), false).toPath(); if (gstreamerPath == null) {