diff --git a/Core/build.xml b/Core/build.xml index a2e901c3a9..968bec5b92 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -110,11 +110,11 @@ - - + + - + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 48d6c84258..fb31a36e17 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -231,15 +231,10 @@ AddImageWizardIngestConfigPanel.dsProcDone.errs.text=*Errors encountered in addi AddImageWizardIngestConfigVisual.getName.text=Configure Ingest Modules AddImageWizardIterator.stepXofN=Step {0} of {1} AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1} -Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open\! +Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open! Case.open.msgDlg.updated.msg=Updated case database schema.\nA backup copy of the database with the following path has been made:\n {0} Case.open.msgDlg.updated.title=Case Database Schema Update -Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \n\ -this case are missing. Would you like to search for them now?\n\ -Previously, the image was located at:\n\ -{0}\n\ -Please note that you will still be able to browse directories and generate reports\n\ -if you choose No, but you will not be able to view file content or run the ingest process. +Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \nthis case are missing. Would you like to search for them now?\nPreviously, the image was located at:\n{0}\nPlease note that you will still be able to browse directories and generate reports\nif you choose No, but you will not be able to view file content or run the ingest process. Case.checkImgExist.confDlg.doesntExist.title=Missing Image Case.addImg.exception.msg=Error adding image to the case Case.updateCaseName.exception.msg=Error while trying to update the case name. @@ -258,12 +253,9 @@ Case.GetCaseTypeGivenPath.Failure=Unable to get case type Case.metaDataFileCorrupt.exception.msg=The case metadata file (.aut) is corrupted. Case.deleteReports.deleteFromDiskException.log.msg=Unable to delete the report from the disk. Case.deleteReports.deleteFromDiskException.msg=Unable to delete the report {0} from the disk.\nYou may manually delete it from {1} -CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \n\ - Case Name: {0}\n\ - Case Directory: {1} +CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \nCase Name: {0}\nCase Directory: {1} CaseDeleteAction.closeConfMsg.title=Warning: Closing the Current Case -CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\n\ -Close the folder and file and try again or you can delete the case manually. +CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\nClose the folder and file and try again or you can delete the case manually. CaseDeleteAction.msgDlg.fileInUse.title=Error: Folder In Use CaseDeleteAction.msgDlg.caseDelete.msg=Case {0} has been deleted. CaseOpenAction.autFilter.title={0} Case File ( {1}) @@ -295,8 +287,7 @@ NewCaseWizardAction.databaseProblem1.text=Cannot open database. Cancelling case NewCaseWizardAction.databaseProblem2.text=Error NewCaseWizardPanel1.validate.errMsg.invalidSymbols=The Case Name cannot contain any of the following symbols: \\ / : * ? " < > | NewCaseWizardPanel1.validate.errMsg.dirExists=Case directory ''{0}'' already exists. -NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\n\ - Do you want to create that directory? +NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\nDo you want to create that directory? NewCaseWizardPanel1.validate.confMsg.createDir.title=Create directory NewCaseWizardPanel1.validate.errMsg.cantCreateParDir.msg=Error: Could not create case parent directory {0} NewCaseWizardPanel1.validate.errMsg.prevCreateBaseDir.msg=Prevented from creating base directory {0} @@ -332,7 +323,6 @@ StartupWindow.title.text=Welcome UnpackagePortableCaseDialog.title.text=Unpackage Portable Case UnpackagePortableCaseDialog.UnpackagePortableCaseDialog.extensions=Portable case package (.zip, .zip.001) UnpackagePortableCaseDialog.validatePaths.badExtension=File extension must be .zip or .zip.001 -# {0} - case folder UnpackagePortableCaseDialog.validatePaths.caseFolderExists=Folder {0} already exists UnpackagePortableCaseDialog.validatePaths.caseIsNotFile=Selected path is not a file UnpackagePortableCaseDialog.validatePaths.caseNotFound=File does not exist @@ -346,15 +336,15 @@ UnpackageWorker.doInBackground.errorFinding7zip=Could not locate 7-Zip executabl UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases UpdateRecentCases.menuItem.empty=-Empty- AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel -NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive -NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on \"C:\" drive. Case folder is created on the target system +NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on "C:" drive +NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on "C:" drive. Case folder is created on the target system NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning: Path to case folder is on the target system. Create case folder in mounted drive. CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1} MissingImageDialog.lbWarning.text= MissingImageDialog.lbWarning.toolTipText= NewCaseVisualPanel1.caseParentDirWarningLabel.text= -NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user +NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user\t\t NewCaseVisualPanel1.singleUserCaseRadioButton.text=Single-user NewCaseVisualPanel1.caseTypeLabel.text=Case Type: SingleUserCaseConverter.BadDatabaseFileName=Database file does not exist! diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties-MERGED index 007af703c5..75866a79d0 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties-MERGED @@ -5,10 +5,7 @@ CentralRepoCommentDialog.title.addEditCentralRepoComment=Add/Edit Central Reposi OpenIDE-Module-Name=Central Repository OpenIDE-Module-Display-Category=Ingest Module OpenIDE-Module-Short-Description=Correlation Engine Ingest Module -OpenIDE-Module-Long-Description=\ - Correlation Engine ingest module and central database. \n\n\ - The Correlation Engine ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\ - Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest. +OpenIDE-Module-Long-Description=Correlation Engine ingest module and central database. \n\nThe Correlation Engine ingest module stores attributes of artifacts matching selected correlation types into a central database.\nStored attributes are used in future cases to correlate and analyzes files and artifacts during ingest. CentralRepoCommentDialog.commentLabel.text=Comment: CentralRepoCommentDialog.okButton.text=&OK CentralRepoCommentDialog.cancelButton.text=C&ancel diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java deleted file mode 100644 index 39eae2b9f3..0000000000 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2017 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.communications; - -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import org.openide.nodes.AbstractNode; -import org.openide.nodes.ChildFactory; -import org.openide.nodes.Children; -import org.openide.nodes.Node; -import org.python.google.common.collect.Iterables; -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; - -/** - * 'Root' Node for the Account/Messages area. Has children which are all the - * relationships of all the accounts in this node. - * - */ -final class AccountDetailsNode extends AbstractNode { - - private final static Logger logger = Logger.getLogger(AccountDetailsNode.class.getName()); - - AccountDetailsNode(Set accountDeviceInstances, CommunicationsFilter filter, CommunicationsManager commsManager) { - super(Children.create(new AccountRelationshipChildren(accountDeviceInstances, commsManager, filter), true)); - String displayName = (accountDeviceInstances.size() == 1) - ? Iterables.getOnlyElement(accountDeviceInstances).getAccount().getTypeSpecificID() - : accountDeviceInstances.size() + " accounts"; - setDisplayName(displayName); - } - - /** - * Children object for the relationships that the accounts are part of. - */ - private static class AccountRelationshipChildren extends ChildFactory { - - private final Set accountDeviceInstances; - private final CommunicationsManager commsManager; - private final CommunicationsFilter filter; - - private AccountRelationshipChildren(Set accountDeviceInstances, CommunicationsManager commsManager, CommunicationsFilter filter) { - this.accountDeviceInstances = accountDeviceInstances; - this.commsManager = commsManager; - this.filter = filter; - } - - @Override - protected boolean createKeys(List list) { - try { - list.addAll(commsManager.getRelationshipSources(accountDeviceInstances, filter)); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting communications", ex); - } - return true; - } - - @Override - protected Node createNodeForKey(Content t) { - if (t instanceof BlackboardArtifact) { - return new RelationshipNode((BlackboardArtifact) t); - } else { - throw new UnsupportedOperationException("Cannot create a RelationshipNode for non BlackboardArtifact content."); - } - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java index 6abd4a70d0..fcf64182a3 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java @@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.communications; import com.google.common.eventbus.Subscribe; import java.awt.Component; +import java.util.HashSet; +import java.util.Set; import java.util.logging.Level; import javax.swing.JPanel; import javax.swing.ListSelectionModel; @@ -31,11 +33,16 @@ import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; +import org.openide.nodes.Node; import org.openide.util.Lookup; import org.openide.util.lookup.ProxyLookup; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.communications.relationships.RelationshipBrowser; +import org.sleuthkit.autopsy.communications.relationships.SelectionInfo; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AccountDeviceInstance; +import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.TskCoreException; @@ -56,8 +63,9 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro private final Outline outline; - private final ExplorerManager messageBrowserEM = new ExplorerManager(); private final ExplorerManager accountsTableEM = new ExplorerManager(); + + final RelationshipBrowser relationshipBrowser; /* * This lookup proxies the selection lookup of both he accounts table and @@ -78,21 +86,30 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.AccountNode_accountName()); outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); outline.setColumnSorted(3, false, 1); //it would be nice if the column index wasn't hardcoded + + relationshipBrowser = new RelationshipBrowser(); + jSplitPane1.setRightComponent(relationshipBrowser); accountsTableEM.addPropertyChangeListener(evt -> { if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) { SwingUtilities.invokeLater(this::setColumnWidths); } else if (ExplorerManager.PROP_EXPLORED_CONTEXT.equals(evt.getPropertyName())) { SwingUtilities.invokeLater(this::setColumnWidths); + } else if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { + final Node[] selectedNodes = accountsTableEM.getSelectedNodes(); + final Set accountDeviceInstances = new HashSet<>(); + + CommunicationsFilter filter = null; + for (final Node node : selectedNodes) { + accountDeviceInstances.add(((AccountDeviceInstanceNode) node).getAccountDeviceInstance()); + filter = ((AccountDeviceInstanceNode)node).getFilter(); + } + relationshipBrowser.setSelectionInfo(new SelectionInfo(accountDeviceInstances, filter)); } }); - final MessageBrowser messageBrowser = new MessageBrowser(accountsTableEM, messageBrowserEM); - - jSplitPane1.setRightComponent(messageBrowser); - - proxyLookup = new ProxyLookup( - messageBrowser.getLookup(), - ExplorerUtils.createLookup(accountsTableEM, getActionMap())); + + proxyLookup = new ProxyLookup(relationshipBrowser.getLookup(), + ExplorerUtils.createLookup(accountsTableEM, getActionMap())); } private void setColumnWidths() { diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index a71d34bb61..4b08fd2d45 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -32,19 +32,13 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in VisualizationPanel.zoomInButton.text= VisualizationPanel.zoomOutButton.toolTipText=Zoom out VisualizationPanel.zoomOutButton.text= -<<<<<<< HEAD -VisualizationPanel.fastOrganicLayoutButton.text=Redraw -VisualizationPanel.clearVizButton.text_1=Clear +VisualizationPanel.fastOrganicLayoutButton.text= VisualizationPanel.backButton.text_1= -VisualizationPanel.forwardButton.text= -======= VisualizationPanel.circleLayoutButton.text=Circle VisualizationPanel.organicLayoutButton.text=Organic -VisualizationPanel.fastOrganicLayoutButton.text= VisualizationPanel.hierarchyLayoutButton.text=Hierarchical VisualizationPanel.clearVizButton.text_1= VisualizationPanel.snapshotButton.text_1=Snapshot Report ->>>>>>> develop VisualizationPanel.clearVizButton.actionCommand= VisualizationPanel.backButton.toolTipText=Click to go back VisualizationPanel.forwardButton.toolTipText=Click to go forward @@ -53,3 +47,4 @@ VisualizationPanel.clearVizButton.toolTipText=Click to clear the chart FiltersPanel.limitHeaderLabel.text=Communications Limit: FiltersPanel.mostRecentLabel.text=Most Recent: FiltersPanel.limitErrorMsgLabel.text=Invalid integer value. +VisualizationPanel.forwardButton.text= diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED index 6fb582ed97..7a51e26151 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED @@ -22,7 +22,6 @@ FiltersPanel.refreshButton.text=Refresh FiltersPanel.deviceRequiredLabel.text=Select at least one. FiltersPanel.accountTypeRequiredLabel.text=Select at least one. FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh. -MessageBrowser.DataResultViewerTable.title=Messages OpenCVTAction.displayName=Communications PinAccountsAction.pluralText=Add Selected Accounts to Visualization PinAccountsAction.singularText=Add Selected Account to Visualization @@ -76,19 +75,13 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in VisualizationPanel.zoomInButton.text= VisualizationPanel.zoomOutButton.toolTipText=Zoom out VisualizationPanel.zoomOutButton.text= -<<<<<<< HEAD -VisualizationPanel.fastOrganicLayoutButton.text=Redraw -VisualizationPanel.clearVizButton.text_1=Clear +VisualizationPanel.fastOrganicLayoutButton.text= VisualizationPanel.backButton.text_1= -VisualizationPanel.forwardButton.text= -======= VisualizationPanel.circleLayoutButton.text=Circle VisualizationPanel.organicLayoutButton.text=Organic -VisualizationPanel.fastOrganicLayoutButton.text= VisualizationPanel.hierarchyLayoutButton.text=Hierarchical VisualizationPanel.clearVizButton.text_1= VisualizationPanel.snapshotButton.text_1=Snapshot Report ->>>>>>> develop VisualizationPanel.clearVizButton.actionCommand= VisualizationPanel.backButton.toolTipText=Click to go back VisualizationPanel.forwardButton.toolTipText=Click to go forward @@ -97,6 +90,7 @@ VisualizationPanel.clearVizButton.toolTipText=Click to clear the chart FiltersPanel.limitHeaderLabel.text=Communications Limit: FiltersPanel.mostRecentLabel.text=Most Recent: FiltersPanel.limitErrorMsgLabel.text=Invalid integer value. +VisualizationPanel.forwardButton.text= 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.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index bc8047d473..16c3208ff8 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -19,6 +19,7 @@ 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.util.List; @@ -61,8 +62,11 @@ public final class CVTTopComponent extends TopComponent { associateLookup(proxyLookup); // Make sure the Global Actions Context is proxying the selection of the active tab. browseVisualizeTabPane.addChangeListener(changeEvent -> { - Lookup.Provider selectedComponent = (Lookup.Provider) browseVisualizeTabPane.getSelectedComponent(); - proxyLookup.setNewLookups(selectedComponent.getLookup()); + Component selectedComponent = browseVisualizeTabPane.getSelectedComponent(); + if(selectedComponent instanceof Lookup.Provider) { + Lookup lookup = ((Lookup.Provider)selectedComponent).getLookup(); + proxyLookup.setNewLookups(lookup); + } filtersPane.setDeviceAccountTypeEnabled(browseVisualizeTabPane.getSelectedIndex() != 0); }); diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index fab152fcd4..7183b4e92b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -53,6 +53,7 @@ import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter; import org.sleuthkit.datamodel.CommunicationsFilter.MostRecentFilter; import org.sleuthkit.datamodel.DataSource; import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG; +import static org.sleuthkit.datamodel.Relationship.Type.CONTACT; import static org.sleuthkit.datamodel.Relationship.Type.MESSAGE; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; @@ -675,7 +676,7 @@ final public class FiltersPanel extends JPanel { commsFilter.addAndFilter(getAccountTypeFilter()); commsFilter.addAndFilter(getDateRangeFilter()); commsFilter.addAndFilter(new CommunicationsFilter.RelationshipTypeFilter( - ImmutableSet.of(CALL_LOG, MESSAGE))); + ImmutableSet.of(CALL_LOG, MESSAGE, CONTACT))); commsFilter.addAndFilter(getMostRecentFilter()); return commsFilter; } diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java deleted file mode 100644 index 339e842280..0000000000 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2017-2018 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may 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; - -import java.awt.Component; -import java.awt.KeyboardFocusManager; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.HashSet; -import java.util.Set; -import javax.swing.JPanel; -import static javax.swing.SwingUtilities.isDescendingFrom; -import org.openide.explorer.ExplorerManager; -import static org.openide.explorer.ExplorerUtils.createLookup; -import org.openide.nodes.Node; -import org.openide.util.Lookup; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.corecomponents.DataResultPanel; -import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; -import org.sleuthkit.autopsy.corecomponents.TableFilterNode; -import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; - -/** - * The right hand side of the CVT. Has a DataResultPanel to show a listing of - * messages and other account details, and a ContentViewer to show individual - * messages. - */ -@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives -public final class MessageBrowser extends JPanel implements ExplorerManager.Provider, Lookup.Provider { - - private static final long serialVersionUID = 1L; - private final ExplorerManager tableEM; - private final ExplorerManager gacExplorerManager; - private final DataResultPanel messagesResultPanel; - /* lookup that will be exposed through the (Global Actions Context) */ - private final ModifiableProxyLookup proxyLookup = new ModifiableProxyLookup(); - - private final PropertyChangeListener focusPropertyListener = new PropertyChangeListener() { - /** - * Listener that keeps the proxyLookup in sync with the focused area of - * the UI. - * - * Since the embedded MessageContentViewer (attachments panel) is not in - * its own TopComponenet, its selection does not get proxied into the - * Global Actions Context (GAC), and many of the available actions don't - * work on it. Further, we can't put the selection from both the - * Messages table and the Attachments table in the GAC because they - * could both include AbstractFiles, muddling the selection seen by the - * actions. Instead, depending on where the focus is in the window, we - * want to put different Content in the Global Actions Context to be - * picked up by, e.g., the tagging actions. The best way I could figure - * to do this was to listen to all focus events and swap out what is in - * the lookup appropriately. An alternative to this would be to - * investigate using the ContextAwareAction interface. - * - * @see org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a - * similar situation and a similar solution. - * - * @param focusEvent The focus change event. - */ - @Override - public void propertyChange(final PropertyChangeEvent focusEvent) { - if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) { - final Component newFocusOwner = (Component) focusEvent.getNewValue(); - - if (newFocusOwner == null) { - return; - } - if (isDescendingFrom(newFocusOwner, messageDataContent)) { - //if the focus owner is within the MessageContentViewer ( the attachments table) - proxyLookup.setNewLookups(createLookup(messageDataContent.getExplorerManager(), getActionMap())); - } else if (isDescendingFrom(newFocusOwner, messagesResultPanel)) { - //... or if it is within the Messages table. - proxyLookup.setNewLookups(createLookup(gacExplorerManager, getActionMap())); - } - - } - } - }; - - /** - * Constructs the right hand side of the Communications Visualization Tool - * (CVT). - * - * @param tableEM An explorer manager to listen to as the driver - * of the Message Table. - * @param gacExplorerManager An explorer manager associated with the - * GlobalActionsContext (GAC) so that selections - * in the messages browser can be exposed to - * context-sensitive actions. - */ - @NbBundle.Messages({"MessageBrowser.DataResultViewerTable.title=Messages"}) - MessageBrowser(final ExplorerManager tableEM, final ExplorerManager gacExplorerManager) { - this.tableEM = tableEM; - this.gacExplorerManager = gacExplorerManager; - initComponents(); - //create an uninitialized DataResultPanel so we can control the ResultViewers that get added. - messagesResultPanel = DataResultPanel.createInstanceUninitialized("Account", "", Node.EMPTY, 0, messageDataContent); - splitPane.setTopComponent(messagesResultPanel); - splitPane.setBottomComponent(messageDataContent); - messagesResultPanel.addResultViewer(new DataResultViewerTable(gacExplorerManager, - Bundle.MessageBrowser_DataResultViewerTable_title())); - messagesResultPanel.open(); - - this.tableEM.addPropertyChangeListener(new PropertyChangeListener() { - /** - * Listener that pushes selections in the tableEM (the Accounts - * table) into the Messages table. - * - * @param pce The ExplorerManager event. - */ - @Override - public void propertyChange(PropertyChangeEvent pce) { - if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { - final Node[] selectedNodes = MessageBrowser.this.tableEM.getSelectedNodes(); - messagesResultPanel.setNumberOfChildNodes(0); - messagesResultPanel.setNode(null); - messagesResultPanel.setPath(""); - if (selectedNodes.length > 0) { - Node rootNode; - final Node selectedNode = selectedNodes[0]; - - if (selectedNode instanceof AccountDeviceInstanceNode) { - rootNode = makeRootNodeFromAccountDeviceInstanceNodes(selectedNodes); - } else { - rootNode = selectedNode; - } - messagesResultPanel.setPath(rootNode.getDisplayName()); - messagesResultPanel.setNode(new TableFilterNode(new DataResultFilterNode(rootNode, gacExplorerManager), true)); - } - } - } - - private Node makeRootNodeFromAccountDeviceInstanceNodes(final Node[] selectedNodes) { - //Use lookup here? - final AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNodes[0]; - - final Set accountDeviceInstances = new HashSet<>(); - for (final Node n : selectedNodes) { - //Use lookup here? - accountDeviceInstances.add(((AccountDeviceInstanceNode) n).getAccountDeviceInstanceKey()); - } - return SelectionNode.createFromAccounts(accountDeviceInstances, adiNode.getFilter(), adiNode.getCommsManager()); - } - } - ); - } - - @Override - public ExplorerManager getExplorerManager() { - return gacExplorerManager; - } - - @Override - public Lookup getLookup() { - return proxyLookup; - } - - @Override - public void addNotify() { - super.addNotify(); - //add listener that maintains correct selection in the Global Actions Context - KeyboardFocusManager.getCurrentKeyboardFocusManager() - .addPropertyChangeListener("focusOwner", focusPropertyListener); - } - - @Override - public void removeNotify() { - super.removeNotify(); - KeyboardFocusManager.getCurrentKeyboardFocusManager() - .removePropertyChangeListener("focusOwner", focusPropertyListener); - } - - /** - * 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() { - - splitPane = new javax.swing.JSplitPane(); - messageDataContent = new org.sleuthkit.autopsy.communications.MessageDataContent(); - - splitPane.setDividerLocation(400); - splitPane.setDividerSize(10); - splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); - splitPane.setResizeWeight(0.5); - splitPane.setBottomComponent(messageDataContent); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGap(0, 0, 0) - .addComponent(splitPane)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(0, 0, 0) - .addComponent(splitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1083, Short.MAX_VALUE) - .addGap(0, 0, 0)) - ); - }// //GEN-END:initComponents - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private org.sleuthkit.autopsy.communications.MessageDataContent messageDataContent; - private javax.swing.JSplitPane splitPane; - // End of variables declaration//GEN-END:variables - -} diff --git a/Core/src/org/sleuthkit/autopsy/communications/ModifiableProxyLookup.java b/Core/src/org/sleuthkit/autopsy/communications/ModifiableProxyLookup.java index ad3babc504..0eccb8bf10 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/ModifiableProxyLookup.java +++ b/Core/src/org/sleuthkit/autopsy/communications/ModifiableProxyLookup.java @@ -26,9 +26,9 @@ import org.openide.util.lookup.ProxyLookup; * delegated to. * */ -final class ModifiableProxyLookup extends ProxyLookup { +final public class ModifiableProxyLookup extends ProxyLookup { - ModifiableProxyLookup(final Lookup... lookups) { + public ModifiableProxyLookup(final Lookup... lookups) { super(lookups); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelaionshipSetNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/RelaionshipSetNodeFactory.java deleted file mode 100644 index b147ea9537..0000000000 --- a/Core/src/org/sleuthkit/autopsy/communications/RelaionshipSetNodeFactory.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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; - -import java.util.Collection; -import java.util.List; -import org.openide.nodes.ChildFactory; -import org.openide.nodes.Node; -import org.sleuthkit.datamodel.BlackboardArtifact; - -/** - * - */ -public class RelaionshipSetNodeFactory extends ChildFactory { - - private final Collection artifacts; - - public RelaionshipSetNodeFactory(Collection artifacts) { - this.artifacts = artifacts; - } - - @Override - protected boolean createKeys(List list) { - list.addAll(artifacts); - return true; - } - - @Override - protected Node createNodeForKey(BlackboardArtifact key) { - return new RelationshipNode(key); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/communications/SelectionNode.java b/Core/src/org/sleuthkit/autopsy/communications/SelectionNode.java deleted file mode 100644 index 1618bb9ecc..0000000000 --- a/Core/src/org/sleuthkit/autopsy/communications/SelectionNode.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2018 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.communications; - -import com.google.common.collect.Iterables; -import com.google.common.collect.Sets; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.stream.Collectors; -import org.openide.nodes.AbstractNode; -import org.openide.nodes.ChildFactory; -import org.openide.nodes.Children; -import org.openide.nodes.Node; -import org.openide.util.Lookup; -import org.openide.util.lookup.Lookups; -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; - -/** - * 'Root' Node for the Account/Messages area. Represents all the relationships - * that are selected in the AccountsBrowser or the VisualizationPanel. Can be - * populated with AccountDeviceInstance and/or directly with relationships - * (Content). - */ -final class SelectionNode extends AbstractNode { - - private SelectionNode(Children children, Lookup lookup) { - super(children, lookup); - } - - static SelectionNode createFromAccountsAndRelationships( - Set edgeRelationshipArtifacts, - Set accountDeviceInstanceKeys, - CommunicationsFilter filter, - CommunicationsManager commsManager) { - - Set accountDeviceInstances = accountDeviceInstanceKeys.stream() - .map(AccountDeviceInstanceKey::getAccountDeviceInstance) - .collect(Collectors.toSet()); - - SelectionNode node = new SelectionNode(Children.create( - new RelationshipChildren( - edgeRelationshipArtifacts, - accountDeviceInstances, - commsManager, - filter), - true), Lookups.fixed(accountDeviceInstanceKeys.toArray())); - - //This is not good for internationalization!!! - String name = ""; - final int accounts = accountDeviceInstances.size(); - if (accounts > 1) { - name = accounts + " accounts"; - } else if (accounts == 1) { - name = Iterables.getOnlyElement(accountDeviceInstances).getAccount().getTypeSpecificID(); - } - - final int edges = edgeRelationshipArtifacts.size(); - - if (edges > 0) { - name = name + (name.isEmpty() ? "" : " and ") + edges + " relationship" + (edges > 1 ? "s" : ""); - } - - node.setDisplayName(name); - return node; - } - - static SelectionNode createFromAccounts( - Set accountDeviceInstances, - CommunicationsFilter filter, - CommunicationsManager commsManager) { - - return createFromAccountsAndRelationships(Collections.emptySet(), accountDeviceInstances, filter, commsManager); - } - - /** - * Children object for the relationships that the accounts are part of. - */ - private static class RelationshipChildren extends ChildFactory { - - static final private Logger logger = Logger.getLogger(RelationshipChildren.class.getName()); - - private final Set edgeRelationshipArtifacts; - - private final Set accountDeviceInstances; - - private final CommunicationsManager commsManager; - private final CommunicationsFilter filter; - - private RelationshipChildren(Set selectedEdgeRelationshipSources, Set selecedAccountDeviceInstances, CommunicationsManager commsManager, CommunicationsFilter filter) { - this.edgeRelationshipArtifacts = selectedEdgeRelationshipSources; - this.accountDeviceInstances = selecedAccountDeviceInstances; - this.commsManager = commsManager; - this.filter = filter; - } - - @Override - protected boolean createKeys(List list) { - try { - final Set relationshipSources = commsManager.getRelationshipSources(accountDeviceInstances, filter); - list.addAll(Sets.union(relationshipSources, edgeRelationshipArtifacts)); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting communications", ex); - } - return true; - } - - @Override - protected Node createNodeForKey(Content content) { - if (content instanceof BlackboardArtifact) { - return new RelationshipNode((BlackboardArtifact) content); - } else { - throw new UnsupportedOperationException("Cannot create a RelationshipNode for non BlackboardArtifact content."); - } - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/communications/Utils.java b/Core/src/org/sleuthkit/autopsy/communications/Utils.java index c4a62209c7..4a3e03e1f2 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Utils.java +++ b/Core/src/org/sleuthkit/autopsy/communications/Utils.java @@ -28,12 +28,12 @@ import org.sleuthkit.datamodel.Account; /** * Utility class with helpers for dealing with accounts. */ -class Utils { +public final class Utils { private Utils() { } - static ZoneId getUserPreferredZoneId() { + static public ZoneId getUserPreferredZoneId() { ZoneId zone = UserPreferences.displayTimesInLocalTime() ? ZoneOffset.systemDefault() : TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()).toZoneId(); return zone; @@ -44,7 +44,7 @@ class Utils { * * @return The path of the icon for the given Account Type. */ - static final String getIconFilePath(Account.Type type) { + static public final String getIconFilePath(Account.Type type) { return Accounts.getIconFilePath(type); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index a0f63348ee..027f2f285a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -54,7 +54,6 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; -import java.beans.PropertyVetoException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -62,11 +61,11 @@ import java.nio.file.Paths; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -98,23 +97,20 @@ import org.apache.commons.lang3.StringUtils; import org.controlsfx.control.Notifications; import org.jdesktop.layout.GroupLayout; import org.jdesktop.layout.LayoutStyle; -import org.openide.explorer.ExplorerManager; -import org.openide.explorer.ExplorerUtils; -import org.openide.nodes.Node; -import org.openide.util.Lookup; import org.openide.util.NbBundle; -import org.openide.util.lookup.ProxyLookup; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.communications.relationships.RelationshipBrowser; +import org.sleuthkit.autopsy.communications.relationships.SelectionInfo; import org.sleuthkit.autopsy.communications.snapshot.CommSnapShotReportWriter; import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator; +import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; -import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; /** * A panel that goes in the Visualize tab of the Communications Visualization @@ -127,7 +123,7 @@ import org.sleuthkit.datamodel.TskCoreException; * actions to work correctly. */ @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives -final public class VisualizationPanel extends JPanel implements Lookup.Provider { +final public class VisualizationPanel extends JPanel { private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName()); @@ -140,9 +136,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @NbBundle.Messages("VisualizationPanel.cancelButton.text=Cancel") private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text(); - private final ExplorerManager vizEM = new ExplorerManager(); - private final ExplorerManager gacEM = new ExplorerManager(); - private final ProxyLookup proxyLookup; private Frame windowAncestor; private CommunicationsManager commsManager; @@ -161,6 +154,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private final Map layoutButtons = new HashMap<>(); private NamedGraphLayout currentLayout; + + private final RelationshipBrowser relationshipBrowser; private final StateManager stateManager; @@ -225,13 +220,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final GraphMouseListener graphMouseListener = new GraphMouseListener(); graphComponent.getGraphControl().addMouseWheelListener(graphMouseListener); graphComponent.getGraphControl().addMouseListener(graphMouseListener); - - final MessageBrowser messageBrowser = new MessageBrowser(vizEM, gacEM); - splitPane.setRightComponent(messageBrowser); - proxyLookup = new ProxyLookup( - ExplorerUtils.createLookup(vizEM, getActionMap()), - messageBrowser.getLookup() - ); + + relationshipBrowser = new RelationshipBrowser(); + splitPane.setRightComponent(relationshipBrowser); //feed selection to explorermanager graph.getSelectionModel().addListener(mxEvent.CHANGE, new SelectionListener()); @@ -257,12 +248,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider setStateButtonsEnabled(); } - - @Override - public Lookup getLookup() { - return proxyLookup; - } - + @Subscribe void handle(LockedVertexModel.VertexLockEvent event) { final Set vertices = event.getVertices(); @@ -387,219 +373,223 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // //GEN-BEGIN:initComponents private void initComponents() { - 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(); - jLabel2 = new JLabel(); - zoomLabel = 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(); + 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(); - setLayout(new BorderLayout()); + setLayout(new java.awt.BorderLayout()); splitPane.setDividerLocation(800); splitPane.setResizeWeight(0.5); - borderLayoutPanel.setLayout(new BorderLayout()); + borderLayoutPanel.setLayout(new java.awt.BorderLayout()); - jTextArea1.setBackground(new Color(240, 240, 240)); + jTextArea1.setBackground(new java.awt.Color(240, 240, 240)); jTextArea1.setColumns(20); jTextArea1.setLineWrap(true); jTextArea1.setRows(5); - jTextArea1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jTextArea1.text")); // NOI18N + jTextArea1.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jTextArea1.text")); // NOI18N - GroupLayout placeHolderPanelLayout = new GroupLayout(placeHolderPanel); + org.jdesktop.layout.GroupLayout placeHolderPanelLayout = new org.jdesktop.layout.GroupLayout(placeHolderPanel); placeHolderPanel.setLayout(placeHolderPanelLayout); - placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) + placeHolderPanelLayout.setHorizontalGroup( + placeHolderPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(placeHolderPanelLayout.createSequentialGroup() .addContainerGap(250, Short.MAX_VALUE) - .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 424, GroupLayout.PREFERRED_SIZE) + .add(jTextArea1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 424, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .addContainerGap(423, Short.MAX_VALUE)) ); - placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) + placeHolderPanelLayout.setVerticalGroup( + placeHolderPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(placeHolderPanelLayout.createSequentialGroup() - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 47, GroupLayout.PREFERRED_SIZE) - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .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)) ); - borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); + borderLayoutPanel.add(placeHolderPanel, java.awt.BorderLayout.CENTER); - 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.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.setFocusable(false); - fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM); + fastOrganicLayoutButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - 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.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.setFocusable(false); - zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER); - zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM); - zoomOutButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { + 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) { zoomOutButtonActionPerformed(evt); } }); - 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.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.setFocusable(false); - zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER); - zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM); - zoomInButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { + 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) { zoomInButtonActionPerformed(evt); } }); - 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.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.setFocusable(false); - zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER); - zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM); - zoomActualButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { + 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) { zoomActualButtonActionPerformed(evt); } }); - 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.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.setFocusable(false); - fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER); - fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM); - fitZoomButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { + 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) { fitZoomButtonActionPerformed(evt); } }); - jLabel2.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jLabel2.text")); // NOI18N + 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 - 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) { + 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) { clearVizButtonActionPerformed(evt); } }); - jSeparator2.setOrientation(SwingConstants.VERTICAL); + jSeparator2.setOrientation(javax.swing.SwingConstants.VERTICAL); - 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) { + 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) { backButtonActionPerformed(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) { + 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) { forwardButtonActionPerformed(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.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent 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) { snapshotButtonActionPerformed(evt); } }); - jSeparator3.setOrientation(SwingConstants.VERTICAL); + jSeparator3.setOrientation(javax.swing.SwingConstants.VERTICAL); - jSeparator4.setOrientation(SwingConstants.VERTICAL); + jSeparator4.setOrientation(javax.swing.SwingConstants.VERTICAL); - GroupLayout toolbarLayout = new GroupLayout(toolbar); + org.jdesktop.layout.GroupLayout toolbarLayout = new org.jdesktop.layout.GroupLayout(toolbar); toolbar.setLayout(toolbarLayout); - toolbarLayout.setHorizontalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING) + toolbarLayout.setHorizontalGroup( + toolbarLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(toolbarLayout.createSequentialGroup() .addContainerGap() .add(backButton) - .addPreferredGap(LayoutStyle.RELATED) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(forwardButton) - .addPreferredGap(LayoutStyle.RELATED) - .add(jSeparator4, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.RELATED) + .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) .add(fastOrganicLayoutButton) - .addPreferredGap(LayoutStyle.RELATED) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(clearVizButton) - .addPreferredGap(LayoutStyle.RELATED) - .add(jSeparator2, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.RELATED) + .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(LayoutStyle.RELATED) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(zoomLabel) - .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) + .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) .add(snapshotButton) - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); - toolbarLayout.setVerticalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING) + toolbarLayout.setVerticalGroup( + toolbarLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(toolbarLayout.createSequentialGroup() .add(3, 3, 3) - .add(toolbarLayout.createParallelGroup(GroupLayout.CENTER) + .add(toolbarLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.CENTER) .add(fastOrganicLayoutButton) .add(zoomOutButton) - .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(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(zoomLabel) .add(clearVizButton) - .add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(jSeparator2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(backButton) .add(forwardButton) .add(snapshotButton) - .add(jSeparator3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(jSeparator4, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .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(3, 3, 3)) ); - borderLayoutPanel.add(toolbar, BorderLayout.PAGE_START); - borderLayoutPanel.add(notificationsJFXPanel, BorderLayout.PAGE_END); + borderLayoutPanel.add(toolbar, java.awt.BorderLayout.PAGE_START); + borderLayoutPanel.add(notificationsJFXPanel, java.awt.BorderLayout.PAGE_END); splitPane.setLeftComponent(borderLayoutPanel); - add(splitPane, BorderLayout.CENTER); + add(splitPane, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents private void fitZoomButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fitZoomButtonActionPerformed @@ -890,26 +880,26 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } // Variables declaration - do not modify//GEN-BEGIN:variables - private JButton backButton; - private JPanel borderLayoutPanel; - private JButton clearVizButton; - private JButton fastOrganicLayoutButton; - private JButton fitZoomButton; - private JButton forwardButton; - private JLabel jLabel2; - 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 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; // End of variables declaration//GEN-END:variables /** @@ -922,40 +912,25 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void invoke(Object sender, mxEventObject evt) { Object[] selectionCells = graph.getSelectionCells(); - Node rootNode = Node.EMPTY; - Node[] selectedNodes = new Node[0]; if (selectionCells.length > 0) { mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(new mxCell[selectionCells.length]); - HashSet relationshipSources = new HashSet<>(); - HashSet adis = new HashSet<>(); + HashSet deviceInstances = new HashSet<>(); for (mxICell cell : selectedCells) { if (cell.isEdge()) { mxICell source = (mxICell) graph.getModel().getTerminal(cell, true); - AccountDeviceInstanceKey account1 = (AccountDeviceInstanceKey) source.getValue(); mxICell target = (mxICell) graph.getModel().getTerminal(cell, false); - AccountDeviceInstanceKey account2 = (AccountDeviceInstanceKey) target.getValue(); - try { - final List relationshipSources1 = commsManager.getRelationshipSources( - account1.getAccountDeviceInstance(), - account2.getAccountDeviceInstance(), - currentFilter); - relationshipSources.addAll(relationshipSources1); - } catch (TskCoreException tskCoreException) { - logger.log(Level.SEVERE, " Error getting relationsips....", tskCoreException); - } + + deviceInstances.add(((AccountDeviceInstanceKey) source.getValue()).getAccountDeviceInstance()); + deviceInstances.add(((AccountDeviceInstanceKey) target.getValue()).getAccountDeviceInstance()); + } else if (cell.isVertex()) { - adis.add((AccountDeviceInstanceKey) cell.getValue()); + deviceInstances.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance()); } } - rootNode = SelectionNode.createFromAccountsAndRelationships(relationshipSources, adis, currentFilter, commsManager); - selectedNodes = new Node[]{rootNode}; - } - vizEM.setRootContext(rootNode); - try { - vizEM.setSelectedNodes(selectedNodes); - } catch (PropertyVetoException ex) { - logger.log(Level.SEVERE, "Selection vetoed.", ex); + relationshipBrowser.setSelectionInfo(new SelectionInfo(deviceInstances, currentFilter)); + } else { + relationshipBrowser.setSelectionInfo(new SelectionInfo(Collections.EMPTY_SET, currentFilter)); } } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/AccountSourceContentChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/AccountSourceContentChildNodeFactory.java new file mode 100755 index 0000000000..328a0f0dac --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/AccountSourceContentChildNodeFactory.java @@ -0,0 +1,108 @@ +/* + * 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.List; +import java.util.Set; +import java.util.logging.Level; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Children; +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.AccountFileInstance; +import org.sleuthkit.datamodel.CommunicationsManager; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * ChildFactory that creates ContentNode representing the files that reference + * the given list of accounts. + */ +final class AccountSourceContentChildNodeFactory extends ChildFactory { + + private static final Logger logger = Logger.getLogger(AccountSourceContentChildNodeFactory.class.getName()); + + private final Set accounts; + + AccountSourceContentChildNodeFactory(Set accounts) { + this.accounts = accounts; + } + + @Override + protected boolean createKeys(List list) { + if (accounts == null || accounts.isEmpty()) { + return true; + } + + CommunicationsManager communicationManager; + try { + communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager(); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.WARNING, "Failed to get communications manager from case.", ex); //NON-NLS + return false; + } + + accounts.forEach((account) -> { + try { + List accountFileInstanceList = communicationManager.getAccountFileInstances(account); + + for (AccountFileInstance fileInstance : accountFileInstanceList) { + list.add(fileInstance.getFile()); + } + + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Failed to getAccountFileInstances for account: %d", account.getAccountID()), ex); //NON-NLS + } + }); + + return true; + } + + @Override + protected Node createNodeForKey(Content content) { + return new ContentNode(content); + } + + /** + * Simple AbstractNode for a Content (file) object. + */ + final class ContentNode extends AbstractNode { + + private final Content content; + + ContentNode(Content content) { + super(Children.LEAF); + this.content = content; + + try { + setDisplayName(content.getUniquePath()); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to getUniquePath for Content: %d", content.getId()), ex); //NON-NLS + setDisplayName(content.getName()); + } + + setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon.png"); //NON-NLS + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentsChildren.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentsChildren.java new file mode 100755 index 0000000000..7dbe2133e0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentsChildren.java @@ -0,0 +1,127 @@ +/* + * 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.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; +import java.util.logging.Level; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.nodes.Sheet; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; +import org.sleuthkit.autopsy.datamodel.FileNode; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Factory for creating thumbnail children nodes. + */ +final class AttachmentsChildren extends Children.Keys { + + private static final Logger logger = Logger.getLogger(AttachmentsChildren.class.getName()); + + private final Set artifacts; + + /* + * Creates the list of thumbnails from the given list of + * BlackboardArtifacts. + * + * The thumbnails will be initialls sorted by size, then name so that they + * appear sorted by size by default. + */ + AttachmentsChildren(Set artifacts) { + super(false); + + this.artifacts = artifacts; + + + } + + @Override + protected Node[] createNodes(AbstractFile t) { + return new Node[]{new AttachementNode(t)}; + } + + @Override + protected void addNotify() { + super.addNotify(); + + Set thumbnails = new TreeSet<>((AbstractFile file1, AbstractFile file2) -> { + int result = Long.compare(file1.getSize(), file2.getSize()); + if (result == 0) { + result = file1.getName().compareTo(file2.getName()); + } + + return result; + }); + + artifacts.forEach((bba) -> { + try { + for (Content childContent : bba.getChildren()) { + if (childContent instanceof AbstractFile) { + thumbnails.add((AbstractFile) childContent); + } + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get children from artifact.", ex); //NON-NLS + } + }); + + setKeys(thumbnails); + } + + /** + * A node for representing a thumbnail. + */ + static class AttachementNode extends FileNode { + + AttachementNode(AbstractFile file) { + super(file, false); + } + + @Override + protected Sheet createSheet() { + Sheet sheet = super.createSheet(); + Set keepProps = new HashSet<>(Arrays.asList( + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.score.name"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.comment.name"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.count.name"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.sizeColLbl"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.knownColLbl"))); + + //Remove all other props except for the ones above + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); + for (Node.Property p : sheetSet.getProperties()) { + if (!keepProps.contains(p.getName())) { + sheetSet.remove(p.getName()); + } + } + + return sheet; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties new file mode 100755 index 0000000000..6ef03951af --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties @@ -0,0 +1,15 @@ +ContactDetailsPane.nameLabel.text=Placeholder +SummaryViewer.countsPanel.border.title=Counts +SummaryViewer.emailLabel.text=Emails: +SummaryViewer.contactsLabel.text=Contacts: +SummaryViewer.attachmentsLabel.text=Attachments: +SummaryViewer.fileReferencesPanel.border.title=File References in Current Case +SummaryViewer.caseReferencesPanel.border.title=Other Occurrences +OutlineViewPanel.messageLabel.text= +SummaryViewer.messagesDataLabel.text=messages +SummaryViewer.callLogsDataLabel.text=callLogs +SummaryViewer.contactsDataLabel.text=contacts +SummaryViewer.emailDataLabel.text=emails +SummaryViewer.attachmentsDataLabel.text=attachments +SummaryViewer.messagesLabel.text=Messages: +SummaryViewer.callLogsLabel.text=Call Logs: diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED new file mode 100755 index 0000000000..6082d83375 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED @@ -0,0 +1,44 @@ +ContactDetailsPane.nameLabel.text=Placeholder +ContactNode_Email=Email Address +ContactNode_Home_Number=Home Number +ContactNode_Mobile_Number=Mobile Number +ContactNode_Name=Name +ContactNode_Office_Number=Office Number +ContactNode_Phone=Phone Number +ContactNode_URL=URL +ContactsViewer_columnHeader_Email=Email +ContactsViewer_columnHeader_Name=Name +ContactsViewer_columnHeader_Phone=Phone +ContactsViewer_tabTitle=Contacts +MediaViewer_Name=Media +MessageNode_Node_Property_Attms=Attachments +MessageNode_Node_Property_Date=Date +MessageNode_Node_Property_From=From +MessageNode_Node_Property_Subject=Subject +MessageNode_Node_Property_To=To +MessageNode_Node_Property_Type=Type +MessageViewer_columnHeader_Attms=Attachments +MessageViewer_columnHeader_Date=Date +MessageViewer_columnHeader_From=From +MessageViewer_columnHeader_Subject=Subject +MessageViewer_columnHeader_To=To +MessageViewer_tabTitle=Messages +SummaryViewer.countsPanel.border.title=Counts +SummaryViewer.emailLabel.text=Emails: +SummaryViewer.contactsLabel.text=Contacts: +SummaryViewer.attachmentsLabel.text=Attachments: +SummaryViewer.fileReferencesPanel.border.title=File References in Current Case +SummaryViewer.caseReferencesPanel.border.title=Other Occurrences +OutlineViewPanel.messageLabel.text= +SummaryViewer.messagesDataLabel.text=messages +SummaryViewer.callLogsDataLabel.text=callLogs +SummaryViewer.contactsDataLabel.text=contacts +SummaryViewer.emailDataLabel.text=emails +SummaryViewer.attachmentsDataLabel.text=attachments +SummaryViewer.messagesLabel.text=Messages: +SummaryViewer.callLogsLabel.text=Call Logs: +SummaryViewer_CaseRefNameColumn_Title=Case Name +SummaryViewer_CentralRepository_Message= +SummaryViewer_Creation_Date_Title=Creation Date +SummaryViewer_FileRefNameColumn_Title=Path +SummaryViewer_TabTitle=Summary diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.form new file mode 100755 index 0000000000..2ae2165358 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.form @@ -0,0 +1,65 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java new file mode 100755 index 0000000000..82d4668e43 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactDetailsPane.java @@ -0,0 +1,116 @@ +/* + * 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 org.openide.explorer.ExplorerManager; +import org.openide.nodes.Node; + +/** + * 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(); + + /** + * Displays the propertied of a ContactNode in a PropertySheet. + */ + public ContactDetailsPane() { + initComponents(); + this.setEnabled(false); + + nameLabel.setText(""); + } + + /** + * Sets the list of nodes for the property sheet. + * + * @param nodes List of nodes to set + */ + public void setNode(Node[] nodes) { + if (nodes != null && nodes.length == 1) { + nameLabel.setText(nodes[0].getDisplayName()); + propertySheet.setNodes(nodes); + } else { + nameLabel.setText(""); + propertySheet.setNodes(null); + } + } + + @Override + public ExplorerManager getExplorerManager() { + return explorerManager; + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + nameLabel.setEnabled(enabled); + propertySheet.setEnabled(enabled); + } + + /** + * 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() { + + messageContentViewer1 = new org.sleuthkit.autopsy.contentviewers.MessageContentViewer(); + nameLabel = new javax.swing.JLabel(); + propertySheet = new org.openide.explorer.propertysheet.PropertySheet(); + + nameLabel.setFont(new java.awt.Font("Tahoma", 0, 24)); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(ContactDetailsPane.class, "ContactDetailsPane.nameLabel.text")); // NOI18N + + propertySheet.setDescriptionAreaVisible(false); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(propertySheet, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(nameLabel) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(nameLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(propertySheet, javax.swing.GroupLayout.DEFAULT_SIZE, 283, Short.MAX_VALUE) + .addContainerGap()) + ); + }// //GEN-END:initComponents + + + // 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 new file mode 100755 index 0000000000..9f275ec228 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java @@ -0,0 +1,161 @@ +/* + * 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.HashMap; +import java.util.Map; +import java.util.TimeZone; +import java.util.logging.Level; +import org.openide.nodes.Sheet; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.coreutils.Logger; +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_CONTACT; +import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON; +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; + +/** + * Extends BlackboardArtifactNode to override createSheet to create a contact + * artifact specific sheet. + */ +final class ContactNode extends BlackboardArtifactNode { + + private static final Logger logger = Logger.getLogger(ContactNode.class.getName()); + + @Messages({ + "ContactNode_Name=Name", + "ContactNode_Phone=Phone Number", + "ContactNode_Email=Email Address", + "ContactNode_Mobile_Number=Mobile Number", + "ContactNode_Office_Number=Office Number", + "ContactNode_URL=URL", + "ContactNode_Home_Number=Home Number",}) + + ContactNode(BlackboardArtifact artifact) { + super(artifact); + + String name = getAttributeDisplayString(artifact, TSK_NAME); + if (name == null || name.trim().isEmpty()) { + // VCards use TSK_NAME_PERSON instead of TSK_NAME + name = getAttributeDisplayString(artifact, TSK_NAME_PERSON); + } + setDisplayName(name); + } + + @Override + protected Sheet createSheet() { + Sheet sheet = super.createSheet(); + + final BlackboardArtifact artifact = getArtifact(); + BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); + if (fromID != TSK_CONTACT) { + return sheet; + } + + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); + if (sheetSet == null) { + sheetSet = Sheet.createPropertiesSet(); + sheet.put(sheetSet); + } + + // Sorting the attributes by type so that the duplicates can be removed + // and they can be grouped by type for display. The attribute prefixes + // are used so that all attributed of that type are found, including + // ones that are not predefined as part of BlackboardAttributes + try { + HashMap phoneNumMap = new HashMap<>(); + HashMap emailMap = new HashMap<>(); + HashMap nameMap = new HashMap<>(); + HashMap otherMap = new HashMap<>(); + for (BlackboardAttribute bba : artifact.getAttributes()) { + if (bba.getAttributeType().getTypeName().startsWith("TSK_PHONE")) { + phoneNumMap.put(bba.getDisplayString(), bba); + } else if (bba.getAttributeType().getTypeName().startsWith("TSK_EMAIL")) { + emailMap.put(bba.getDisplayString(), bba); + } else if (bba.getAttributeType().getTypeName().startsWith("TSK_NAME")) { + nameMap.put(bba.getDisplayString(), bba); + } else { + otherMap.put(bba.getDisplayString(), bba); + } + } + + addPropertiesToSheet(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getLabel(), + sheetSet, nameMap); + addPropertiesToSheet(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getLabel(), + sheetSet, phoneNumMap); + addPropertiesToSheet(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL.getLabel(), + sheetSet, emailMap); + + for (BlackboardAttribute bba : otherMap.values()) { + sheetSet.put(new NodeProperty<>(bba.getAttributeType().getTypeName(), bba.getAttributeType().getDisplayName(), "", bba.getDisplayString())); + } + + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error getting attribute values.", ex); //NON-NLS + } + + return sheet; + } + + private void addPropertiesToSheet(String propertyID, Sheet.Set sheetSet, Map attributeMap) { + int count = 0; + for (BlackboardAttribute bba : attributeMap.values()) { + if (count++ > 0) { + sheetSet.put(new NodeProperty<>(propertyID + "_" + count, bba.getAttributeType().getDisplayName(), "", bba.getDisplayString())); + } else { + sheetSet.put(new NodeProperty<>(propertyID, bba.getAttributeType().getDisplayName(), "", bba.getDisplayString())); + } + } + } + + private 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 ""; + } + } + + /** + * 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/ContactsChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactsChildNodeFactory.java new file mode 100755 index 0000000000..681a572b96 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactsChildNodeFactory.java @@ -0,0 +1,109 @@ +/* + * 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.List; +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 static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT; +import org.sleuthkit.datamodel.CommunicationsManager; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * ChildFactory for ContactNodes. + */ +final class ContactsChildNodeFactory extends ChildFactory{ + private static final Logger logger = Logger.getLogger(MessagesChildNodeFactory.class.getName()); + + private SelectionInfo selectionInfo; + + /** + * Construct a new ContactsChildNodeFactory from the currently selectionInfo + * + * @param selectionInfo SelectionInfo object for the currently selected + * accounts + */ + ContactsChildNodeFactory(SelectionInfo selectionInfo) { + this.selectionInfo = selectionInfo; + } + + /** + * Updates the current instance of selectionInfo and calls the refresh method. + * + * @param selectionInfo New instance of the currently selected accounts + */ + public void refresh(SelectionInfo selectionInfo) { + this.selectionInfo = selectionInfo; + refresh(true); + } + + /** + * Creates a list of Keys (BlackboardArtifact) for only contacts of the + * currently selected accounts + * @param list List of BlackboardArtifact to populate + * @return True on success + */ + @Override + protected boolean createKeys(List list) { + CommunicationsManager communicationManager; + try { + communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager(); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.SEVERE, "Failed to get communications manager from case.", ex); //NON-NLS + return false; + } + + if(selectionInfo == null) { + return true; + } + + final Set relationshipSources; + + try { + relationshipSources = communicationManager.getRelationshipSources(selectionInfo.getAccountDevicesInstances(), selectionInfo.getCommunicationsFilter()); + + relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> { + + BlackboardArtifact bba = (BlackboardArtifact) content; + BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba.getArtifactTypeID()); + + if (fromID == TSK_CONTACT) { + list.add(bba); + } + }); + + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Failed to get relationship sources.", ex); //NON-NLS + } + + return true; + } + + @Override + protected Node createNodeForKey(BlackboardArtifact key) { + return new ContactNode(key); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactsViewer.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactsViewer.form new file mode 100755 index 0000000000..82fb67dad9 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactsViewer.form @@ -0,0 +1,42 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactsViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactsViewer.java new file mode 100755 index 0000000000..68c48d8166 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactsViewer.java @@ -0,0 +1,186 @@ +/* + * 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.awt.Component; +import java.awt.KeyboardFocusManager; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.JPanel; +import javax.swing.ListSelectionModel; +import static javax.swing.SwingUtilities.isDescendingFrom; +import org.netbeans.swing.outline.DefaultOutlineModel; +import org.netbeans.swing.outline.Outline; +import org.openide.explorer.ExplorerManager; +import static org.openide.explorer.ExplorerUtils.createLookup; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.communications.ModifiableProxyLookup; +import org.sleuthkit.autopsy.corecomponents.TableFilterNode; +import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; +import org.sleuthkit.datamodel.BlackboardAttribute; + +/** + * Visualization for contact nodes. + * + */ +@ServiceProvider(service = RelationshipsViewer.class) +public final class ContactsViewer extends JPanel implements RelationshipsViewer, ExplorerManager.Provider, Lookup.Provider { + + private final ExplorerManager tableEM; + private final Outline outline; + private final ModifiableProxyLookup proxyLookup; + private final PropertyChangeListener focusPropertyListener; + private final ContactsChildNodeFactory nodeFactory; + + @NbBundle.Messages({ + "ContactsViewer_tabTitle=Contacts", + "ContactsViewer_columnHeader_Name=Name", + "ContactsViewer_columnHeader_Phone=Phone", + "ContactsViewer_columnHeader_Email=Email",}) + + /** + * Visualization for contact nodes. + */ + public ContactsViewer() { + tableEM = new ExplorerManager(); + proxyLookup = new ModifiableProxyLookup(createLookup(tableEM, getActionMap())); + nodeFactory = new ContactsChildNodeFactory(null); + + // See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed + // explaination of focusPropertyListener + focusPropertyListener = (final PropertyChangeEvent focusEvent) -> { + if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) { + final Component newFocusOwner = (Component) focusEvent.getNewValue(); + + if (newFocusOwner == null) { + return; + } + if (isDescendingFrom(newFocusOwner, contactPane)) { + //if the focus owner is within the MessageContentViewer (the attachments table) + proxyLookup.setNewLookups(createLookup(contactPane.getExplorerManager(), getActionMap())); + } else if (isDescendingFrom(newFocusOwner, ContactsViewer.this)) { + //... or if it is within the Results table. + proxyLookup.setNewLookups(createLookup(tableEM, getActionMap())); + + } + } + }; + + initComponents(); + + outline = outlineView.getOutline(); + outlineView.setPropertyColumns( + "TSK_EMAIL", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL.getDisplayName(), + "TSK_PHONE_NUMBER", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getDisplayName() + ); + outline.setRootVisible(false); + ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.ContactsViewer_columnHeader_Name()); + + tableEM.addPropertyChangeListener((PropertyChangeEvent evt) -> { + if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { + final Node[] nodes = tableEM.getSelectedNodes(); + contactPane.setNode(nodes); + } + }); + + tableEM.setRootContext(new TableFilterNode(new DataResultFilterNode(new AbstractNode(Children.create(nodeFactory, true)), getExplorerManager()), true)); + } + + @Override + public String getDisplayName() { + return Bundle.ContactsViewer_tabTitle(); + } + + @Override + public JPanel getPanel() { + return this; + } + + @Override + public void setSelectionInfo(SelectionInfo info) { + contactPane.setNode(new Node[]{new AbstractNode(Children.LEAF)}); + contactPane.setEnabled(false); + + nodeFactory.refresh(info); + } + + @Override + public ExplorerManager getExplorerManager() { + return tableEM; + } + + @Override + public Lookup getLookup() { + return proxyLookup; + } + + @Override + public void addNotify() { + super.addNotify(); + //add listener that maintains correct selection in the Global Actions Context + KeyboardFocusManager.getCurrentKeyboardFocusManager() + .addPropertyChangeListener("focusOwner", focusPropertyListener); + } + + @Override + public void removeNotify() { + super.removeNotify(); + KeyboardFocusManager.getCurrentKeyboardFocusManager() + .removePropertyChangeListener("focusOwner", focusPropertyListener); + } + + /** + * 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() { + + outlineView = new org.openide.explorer.view.OutlineView(); + contactPane = new org.sleuthkit.autopsy.communications.relationships.ContactDetailsPane(); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(contactPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 350, Short.MAX_VALUE) + .addGap(1, 1, 1) + .addComponent(contactPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private org.sleuthkit.autopsy.communications.relationships.ContactDetailsPane contactPane; + private org.openide.explorer.view.OutlineView outlineView; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CorrelationCaseChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CorrelationCaseChildNodeFactory.java new file mode 100755 index 0000000000..564c8e4f54 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CorrelationCaseChildNodeFactory.java @@ -0,0 +1,169 @@ +/* + * 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.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.nodes.Sheet; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.NodeProperty; +import org.sleuthkit.datamodel.Account; + +/** + * ChildFactory for CorrelationCases. Finds the cases that reference the given + * list of accounts. + */ +final class CorrelationCaseChildNodeFactory extends ChildFactory { + + private static final Logger logger = Logger.getLogger(CorrelationCaseChildNodeFactory.class.getName()); + + private Map correlationTypeMap; + private final Set accounts; + + /** + * ChildFactory for CorrelationCases. + * + * @param accounts List of Account objects + */ + CorrelationCaseChildNodeFactory(Set accounts) { + this.accounts = accounts; + } + + @Override + protected boolean createKeys(List list) { + if (!EamDb.isEnabled()) { + return true; + } + + EamDb dbInstance; + try { + dbInstance = EamDb.getInstance(); + } catch (EamDbException ex) { + logger.log(Level.SEVERE, "Unable to connect to the Central Repository database.", ex); //NON-NLS + return false; + } + + Map uniqueCaseMap = new HashMap<>(); + + accounts.forEach((account) -> { + try { + CorrelationAttributeInstance.Type correlationType = getCorrelationType(account.getAccountType()); + if (correlationType != null) { + List correlationInstances = dbInstance.getArtifactInstancesByTypeValue(correlationType, account.getTypeSpecificID()); + correlationInstances.forEach((correlationInstance) -> { + CorrelationCase correlationCase = correlationInstance.getCorrelationCase(); + uniqueCaseMap.put(correlationCase.getCaseUUID(), correlationCase); + }); + } + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { + logger.log(Level.WARNING, String.format("Unable to getArtifactInstance for accountID: %d", account.getAccountID()), ex); //NON-NLS + } + }); + + list.addAll(uniqueCaseMap.values()); + + return true; + } + + @Override + protected Node createNodeForKey(CorrelationCase correlationCase) { + return new CaseNode(correlationCase); + } + + /** + * Find the CorrelationAttributeInstance.Type for the given Account.Type. + * + * @param accountType Account type + * + * @return CorrelationAttributeInstance.Type for given account or null if + * there is no match + * + * @throws EamDbException + */ + private CorrelationAttributeInstance.Type getCorrelationType(Account.Type accountType) throws EamDbException { + if (correlationTypeMap == null) { + correlationTypeMap = new HashMap<>(); + List correcationTypeList = CorrelationAttributeInstance.getDefaultCorrelationTypes(); + correcationTypeList.forEach((type) -> { + correlationTypeMap.put(type.getId(), type); + }); + } + + if (Account.Type.EMAIL.equals(accountType)) { + return correlationTypeMap.get(CorrelationAttributeInstance.EMAIL_TYPE_ID); + } else if (Account.Type.PHONE.equals(accountType)) { + return correlationTypeMap.get(CorrelationAttributeInstance.PHONE_TYPE_ID); + } else { + return null; + } + } + + /** + * Simple AbstractNode for a CorrelationCase. The property sheet only + * contains the creation date. + */ + final class CaseNode extends AbstractNode { + + private final CorrelationCase correlationCase; + + /** + * Construct the object, set the display name and icon. + * + * @param correlationCase + */ + CaseNode(CorrelationCase correlationCase) { + super(Children.LEAF); + this.correlationCase = correlationCase; + + setDisplayName(correlationCase.getDisplayName()); + setIconBaseWithExtension("org/sleuthkit/autopsy/images/briefcase.png"); //NON-NLS + } + + @Override + protected Sheet createSheet() { + super.createSheet(); + Sheet sheet = new Sheet(); + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); + if (sheetSet == null) { + sheetSet = Sheet.createPropertiesSet(); + sheet.put(sheetSet); + } + + sheetSet.put(new NodeProperty<>("creationDate", //NON-NLS + correlationCase.getTitleCreationDate(), + correlationCase.getTitleCreationDate(), + correlationCase.getCreationDate())); + + return sheet; + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MediaViewer.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/MediaViewer.form new file mode 100755 index 0000000000..8f311d3dd4 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MediaViewer.form @@ -0,0 +1,68 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MediaViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MediaViewer.java new file mode 100755 index 0000000000..95405b0887 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MediaViewer.java @@ -0,0 +1,233 @@ +/* + * 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.awt.Component; +import java.awt.KeyboardFocusManager; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import javax.swing.JPanel; +import static javax.swing.SwingUtilities.isDescendingFrom; +import org.openide.explorer.ExplorerManager; +import static org.openide.explorer.ExplorerUtils.createLookup; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Node; +import org.openide.util.Lookup; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.communications.ModifiableProxyLookup; +import org.sleuthkit.autopsy.corecomponents.TableFilterNode; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; +import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; +import org.sleuthkit.datamodel.AbstractContent; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.CommunicationsManager; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * A Panel that shows the media (thumbnails) for the selected account. + */ +final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerManager.Provider, Lookup.Provider { + + private static final Logger logger = Logger.getLogger(MediaViewer.class.getName()); + + private final ExplorerManager tableEM = new ExplorerManager(); + private final PropertyChangeListener focusPropertyListener; + + private final ModifiableProxyLookup proxyLookup; + + @Messages({ + "MediaViewer_Name=Media" + }) + /** + * Creates new form ThumbnailViewer + */ + public MediaViewer() { + proxyLookup = new ModifiableProxyLookup(createLookup(tableEM, getActionMap())); + + // See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed + // explaination of focusPropertyListener + focusPropertyListener = (final PropertyChangeEvent focusEvent) -> { + if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) { + final Component newFocusOwner = (Component) focusEvent.getNewValue(); + + if (newFocusOwner == null) { + return; + } + if (isDescendingFrom(newFocusOwner, contentViewer)) { + //if the focus owner is within the MessageContentViewer (the attachments table) + proxyLookup.setNewLookups(createLookup(((MessageDataContent) contentViewer).getExplorerManager(), getActionMap())); + } else if (isDescendingFrom(newFocusOwner, MediaViewer.this)) { + //... or if it is within the Results table. + proxyLookup.setNewLookups(createLookup(tableEM, getActionMap())); + + } + } + }; + + initComponents(); + + tableEM.addPropertyChangeListener((PropertyChangeEvent evt) -> { + if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { + handleNodeSelectionChange(); + } + }); + + thumbnailViewer.resetComponent(); + } + + @Override + public String getDisplayName() { + return Bundle.MediaViewer_Name(); + } + + @Override + public JPanel getPanel() { + return this; + } + + @Override + public void setSelectionInfo(SelectionInfo info) { + final Set relationshipSources; + + CommunicationsManager communicationManager; + Set artifactList = new HashSet<>(); + + try { + communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager(); + relationshipSources = communicationManager.getRelationshipSources(info.getAccountDevicesInstances(), info.getCommunicationsFilter()); + + relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> { + artifactList.add((BlackboardArtifact) content); + }); + + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to update selection." , ex); + } + + if(artifactList.size() == 0) { + thumbnailViewer.resetComponent(); + } + + thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(new AttachmentsChildren(artifactList)), tableEM), true, this.getClass().getName())); + } + + @Override + public ExplorerManager getExplorerManager() { + return tableEM; + } + + @Override + public Lookup getLookup() { + return proxyLookup; + } + + @Override + public void addNotify() { + super.addNotify(); + //add listener that maintains correct selection in the Global Actions Context + KeyboardFocusManager.getCurrentKeyboardFocusManager() + .addPropertyChangeListener("focusOwner", focusPropertyListener); + } + + @Override + public void removeNotify() { + super.removeNotify(); + KeyboardFocusManager.getCurrentKeyboardFocusManager() + .removePropertyChangeListener("focusOwner", focusPropertyListener); + } + + /** + * Handle the change in thumbnail node selection. + */ + private void handleNodeSelectionChange() { + final Node[] nodes = tableEM.getSelectedNodes(); + + if (nodes != null && nodes.length == 1) { + AbstractContent thumbnail = nodes[0].getLookup().lookup(AbstractContent.class); + if (thumbnail != null) { + try { + Content parentContent = thumbnail.getParent(); + if (parentContent != null && parentContent instanceof BlackboardArtifact) { + contentViewer.setNode(new BlackboardArtifactNode((BlackboardArtifact) parentContent)); + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get parent Content from AbstraceContent instance.", ex); //NON-NLS + } + } + } else { + contentViewer.setNode(null); + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + thumbnailViewer = new org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail(tableEM); + contentViewer = new MessageDataContent(); + separator = new javax.swing.JSeparator(); + + thumbnailViewer.setMinimumSize(new java.awt.Dimension(350, 102)); + thumbnailViewer.setPreferredSize(new java.awt.Dimension(450, 400)); + + contentViewer.setPreferredSize(new java.awt.Dimension(450, 400)); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(thumbnailViewer, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(contentViewer, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(separator) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(thumbnailViewer, javax.swing.GroupLayout.DEFAULT_SIZE, 350, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(separator, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(contentViewer, javax.swing.GroupLayout.PREFERRED_SIZE, 450, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(3, 3, 3)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private org.sleuthkit.autopsy.contentviewers.MessageContentViewer contentViewer; + private javax.swing.JSeparator separator; + private org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail thumbnailViewer; + // End of variables declaration//GEN-END:variables + +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageDataContent.java similarity index 96% rename from Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java rename to Core/src/org/sleuthkit/autopsy/communications/relationships/MessageDataContent.java index 33df9bd3d9..108ce7dc20 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageDataContent.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.communications; +package org.sleuthkit.autopsy.communications.relationships; import java.beans.PropertyChangeEvent; import org.openide.explorer.ExplorerManager; diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java old mode 100644 new mode 100755 similarity index 67% rename from Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java rename to Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java index 3fc7e36e4e..e6ac3f507f --- a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017-2018 Basis Technology Corp. + * Copyright 2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.communications; +package org.sleuthkit.autopsy.communications.relationships; import java.util.List; import java.util.TimeZone; @@ -25,12 +25,12 @@ import org.apache.commons.lang3.StringUtils; import org.openide.nodes.Sheet; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.core.UserPreferences; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; 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; @@ -43,24 +43,35 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBU import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.communications.Utils; /** - * Node for a relationship, as represented by a BlackboardArtifact. + * Wraps a BlackboardArtifact as an AbstractNode for use in an OutlookView */ -final class RelationshipNode extends BlackboardArtifactNode { +final class MessageNode extends BlackboardArtifactNode { - private static final Logger logger = Logger.getLogger(RelationshipNode.class.getName()); + private static final Logger logger = Logger.getLogger(MessageNode.class.getName()); - RelationshipNode(BlackboardArtifact artifact) { + MessageNode(BlackboardArtifact artifact) { super(artifact); - final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s"); - String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message"); + + final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s"); // NON-NLS + String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message"); // NON-NLS setDisplayName(removeEndIgnoreCase.isEmpty() ? stripEnd : removeEndIgnoreCase); } + @Messages({ + "MessageNode_Node_Property_Type=Type", + "MessageNode_Node_Property_From=From", + "MessageNode_Node_Property_To=To", + "MessageNode_Node_Property_Date=Date", + "MessageNode_Node_Property_Subject=Subject", + "MessageNode_Node_Property_Attms=Attachments" + }) + @Override protected Sheet createSheet() { - Sheet sheet = new Sheet(); + Sheet sheet = super.createSheet(); List tags = getAllTagsFromDatabase(); Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); if (sheetSet == null) { @@ -68,62 +79,63 @@ final class RelationshipNode extends BlackboardArtifactNode { sheet.put(sheetSet); } - sheetSet.put(new NodeProperty<>("Type", "Type", "Type", getDisplayName())); - + sheetSet.put(new NodeProperty<>("Type", Bundle.MessageNode_Node_Property_Type(), "", getDisplayName())); //NON-NLS + addScoreProperty(sheetSet, tags); - + CorrelationAttributeInstance correlationAttribute = null; - if (UserPreferences.hideCentralRepoCommentsAndOccurrences()== false) { + if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) { correlationAttribute = getCorrelationAttributeInstance(); } addCommentProperty(sheetSet, tags, correlationAttribute); - - if (UserPreferences.hideCentralRepoCommentsAndOccurrences()== false) { + + if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) { addCountProperty(sheetSet, correlationAttribute); } final BlackboardArtifact artifact = getArtifact(); - BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(getArtifact().getArtifactTypeID()); + + 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", "From", "From", - StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_FROM), " \t\n;"))); - sheetSet.put(new NodeProperty<>("To", "To", "To", - StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_TO), " \t\n;"))); - sheetSet.put(new NodeProperty<>("Date", "Date", "Date", - getAttributeDisplayString(artifact, TSK_DATETIME_SENT))); - sheetSet.put(new NodeProperty<>("Subject", "Subject", "Subject", - getAttributeDisplayString(artifact, TSK_SUBJECT))); + 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", "Attms", "Attms", artifact.getChildrenCount())); + 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); + logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex); //NON-NLS } break; case TSK_MESSAGE: - sheetSet.put(new NodeProperty<>("From", "From", "From", - getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM))); - sheetSet.put(new NodeProperty<>("To", "To", "To", - getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO))); - sheetSet.put(new NodeProperty<>("Date", "Date", "Date", - getAttributeDisplayString(artifact, TSK_DATETIME))); - sheetSet.put(new NodeProperty<>("Subject", "Subject", "Subject", - getAttributeDisplayString(artifact, TSK_SUBJECT))); + 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", "Attms", "Attms", artifact.getChildrenCount())); + 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); + logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex); //NON-NLS } break; case TSK_CALLLOG: - sheetSet.put(new NodeProperty<>("From", "From", "From", - getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM))); - sheetSet.put(new NodeProperty<>("To", "To", "To", - getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO))); - sheetSet.put(new NodeProperty<>("Date", "Date", "Date", - getAttributeDisplayString(artifact, TSK_DATETIME_START))); + 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; @@ -144,7 +156,7 @@ final class RelationshipNode extends BlackboardArtifactNode { * @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 ATTRIBUTE_TYPE attributeType) { + private 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) { @@ -156,7 +168,7 @@ final class RelationshipNode extends BlackboardArtifactNode { return attribute.getDisplayString(); } } catch (TskCoreException tskCoreException) { - logger.log(Level.WARNING, "Error getting attribute value.", tskCoreException); + logger.log(Level.WARNING, "Error getting attribute value.", tskCoreException); //NON-NLS return ""; } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesChildNodeFactory.java new file mode 100755 index 0000000000..bb986c3920 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesChildNodeFactory.java @@ -0,0 +1,115 @@ +/* + * 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.List; +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.CommunicationsManager; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * ChildFactory that creates createKeys and nodes from a given selectionInfo for + * only emails, call logs and messages. + * + */ +final class MessagesChildNodeFactory extends ChildFactory { + + private static final Logger logger = Logger.getLogger(MessagesChildNodeFactory.class.getName()); + + private SelectionInfo selectionInfo; + + /** + * Construct a new MessageChildNodeFactory from the currently selectionInfo + * + * @param selectionInfo SelectionInfo object for the currently selected + * accounts + */ + MessagesChildNodeFactory(SelectionInfo selectionInfo) { + this.selectionInfo = selectionInfo; + } + + /** + * Updates the current instance of selectionInfo and calls the refresh method. + * + * @param selectionInfo New instance of the currently selected accounts + */ + public void refresh(SelectionInfo selectionInfo) { + this.selectionInfo = selectionInfo; + refresh(true); + } + + /** + * Creates a list of Keys (BlackboardArtifact) for only messages for the + * currently selected accounts + * + * @param list List of BlackboardArtifact to populate + * + * @return True on success + */ + @Override + protected boolean createKeys(List list) { + CommunicationsManager communicationManager; + try { + communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager(); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.SEVERE, "Failed to get communications manager from case.", ex); //NON-NLS + return false; + } + + if(selectionInfo == null) { + return true; + } + + final Set relationshipSources; + + try { + relationshipSources = communicationManager.getRelationshipSources(selectionInfo.getAccountDevicesInstances(), selectionInfo.getCommunicationsFilter()); + + relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> { + + 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 + || fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) { + list.add(bba); + } + }); + + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Failed to get relationship sources.", ex); //NON-NLS + } + + return true; + } + + @Override + protected Node createNodeForKey(BlackboardArtifact key) { + return new MessageNode(key); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesViewer.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesViewer.form new file mode 100755 index 0000000000..76cf240254 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesViewer.form @@ -0,0 +1,42 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesViewer.java new file mode 100755 index 0000000000..fa7bbab6bb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesViewer.java @@ -0,0 +1,194 @@ +/* + * 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.awt.Component; +import java.awt.KeyboardFocusManager; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.JPanel; +import javax.swing.ListSelectionModel; +import static javax.swing.SwingUtilities.isDescendingFrom; +import org.netbeans.swing.outline.DefaultOutlineModel; +import org.netbeans.swing.outline.Outline; +import org.openide.explorer.ExplorerManager; +import static org.openide.explorer.ExplorerUtils.createLookup; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.util.Lookup; +import org.openide.util.NbBundle.Messages; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.communications.ModifiableProxyLookup; +import org.sleuthkit.autopsy.corecomponents.TableFilterNode; +import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; + +/** + * Visualation for the messages of the currently selected accounts. + */ +@ServiceProvider(service = RelationshipsViewer.class) +public final class MessagesViewer extends JPanel implements RelationshipsViewer, ExplorerManager.Provider, Lookup.Provider { + + private final ExplorerManager tableEM; + private final Outline outline; + private final ModifiableProxyLookup proxyLookup; + private final PropertyChangeListener focusPropertyListener; + private final MessagesChildNodeFactory nodeFactory; + + @Messages({ + "MessageViewer_tabTitle=Messages", + "MessageViewer_columnHeader_From=From", + "MessageViewer_columnHeader_To=To", + "MessageViewer_columnHeader_Date=Date", + "MessageViewer_columnHeader_Subject=Subject", + "MessageViewer_columnHeader_Attms=Attachments" + }) + + /** + * Visualation for the messages of the currently selected accounts. + */ + public MessagesViewer() { + tableEM = new ExplorerManager(); + proxyLookup = new ModifiableProxyLookup(createLookup(tableEM, getActionMap())); + nodeFactory = new MessagesChildNodeFactory(null); + + // See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed + // explaination of focusPropertyListener + focusPropertyListener = (final PropertyChangeEvent focusEvent) -> { + if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) { + final Component newFocusOwner = (Component) focusEvent.getNewValue(); + + if (newFocusOwner == null) { + return; + } + if (isDescendingFrom(newFocusOwner, contentViewer)) { + //if the focus owner is within the MessageContentViewer (the attachments table) + proxyLookup.setNewLookups(createLookup(((MessageDataContent) contentViewer).getExplorerManager(), getActionMap())); + } else if (isDescendingFrom(newFocusOwner, MessagesViewer.this)) { + //... or if it is within the Results table. + proxyLookup.setNewLookups(createLookup(tableEM, getActionMap())); + + } + } + }; + + initComponents(); + + outline = outlineView.getOutline(); + outlineView.setPropertyColumns( + "From", Bundle.MessageViewer_columnHeader_From(), + "To", Bundle.MessageViewer_columnHeader_To(), + "Date", Bundle.MessageViewer_columnHeader_Date(), + "Subject", Bundle.MessageViewer_columnHeader_Subject(), + "Attms", Bundle.MessageViewer_columnHeader_Attms(), + "Type", "Type" + ); + outline.setRootVisible(false); + ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel("Type"); + + tableEM.addPropertyChangeListener((PropertyChangeEvent evt) -> { + if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { + final Node[] nodes = tableEM.getSelectedNodes(); + + if (nodes != null && nodes.length == 1) { + contentViewer.setNode(nodes[0]); + } + else { + contentViewer.setNode(null); + } + } + }); + + tableEM.setRootContext(new TableFilterNode(new DataResultFilterNode(new AbstractNode(Children.create(nodeFactory, true)), getExplorerManager()), true)); + } + + @Override + public String getDisplayName() { + return Bundle.MessageViewer_tabTitle(); + } + + @Override + public JPanel getPanel() { + return this; + } + + @Override + public void setSelectionInfo(SelectionInfo info) { + nodeFactory.refresh(info); + } + + @Override + public ExplorerManager getExplorerManager() { + return tableEM; + } + + @Override + public Lookup getLookup() { + return proxyLookup; + } + + @Override + public void addNotify() { + super.addNotify(); + //add listener that maintains correct selection in the Global Actions Context + KeyboardFocusManager.getCurrentKeyboardFocusManager() + .addPropertyChangeListener("focusOwner", focusPropertyListener); + } + + @Override + public void removeNotify() { + super.removeNotify(); + KeyboardFocusManager.getCurrentKeyboardFocusManager() + .removePropertyChangeListener("focusOwner", focusPropertyListener); + } + + /** + * 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() { + + outlineView = new org.openide.explorer.view.OutlineView(); + contentViewer = new MessageDataContent(); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(contentViewer, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(contentViewer, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private org.sleuthkit.autopsy.contentviewers.MessageContentViewer contentViewer; + private org.openide.explorer.view.OutlineView outlineView; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/OutlineViewPanel.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/OutlineViewPanel.form new file mode 100755 index 0000000000..5efb16c2b1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/OutlineViewPanel.form @@ -0,0 +1,60 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/OutlineViewPanel.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/OutlineViewPanel.java new file mode 100755 index 0000000000..c84c7ae0a7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/OutlineViewPanel.java @@ -0,0 +1,132 @@ +/* + * 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.awt.CardLayout; +import org.openide.explorer.ExplorerManager; +import static org.openide.explorer.ExplorerUtils.createLookup; +import org.openide.explorer.view.OutlineView; +import org.openide.nodes.Node;; +import org.openide.util.Lookup; + +/** + * This class is a simple wrapper around a OutlineView with its own ExplorerManager. + * + * This panel has the added feature of being able to hide the OutlineView and show + * a message. + * + */ +public class OutlineViewPanel extends javax.swing.JPanel implements ExplorerManager.Provider, Lookup.Provider{ + + private final ExplorerManager tableEm; + private final Lookup lookup; + /** + * Creates new form OutlineViewPanel + */ + public OutlineViewPanel() { + tableEm = new ExplorerManager(); + lookup = createLookup(tableEm, getActionMap()); + + initComponents(); + } + + @Override + public ExplorerManager getExplorerManager() { + return tableEm; + } + + @Override + public Lookup getLookup() { + return lookup; + } + + /** + * Hide the OutlineView and replace with a panel with the given message. + * + * @param message String message to show on the panel. + */ + public void hideOutlineView(String message) { + CardLayout layout = (CardLayout)this.getLayout(); + layout.show(this, "messageCard"); //NON-NLS + messageLabel.setText(message); + } + + /** + * Hides the message panel and shows the OutlineView. + */ + public void showOutlineView() { + CardLayout layout = (CardLayout)this.getLayout(); + layout.show(this, "outlineCard"); //NON-NLS + } + + /** + * Returns the OutlineView instance for ease of customization. + * + * @return Returns the OutlineView + */ + public OutlineView getOutlineView() { + return outlineView; + } + + public void setNode(Node node) { + tableEm.setRootContext(node); + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + outlineView.setEnabled(enabled); + } + + /** + * 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() { + + outlineView = new org.openide.explorer.view.OutlineView(); + messagePanel = new javax.swing.JPanel(); + messageLabel = new javax.swing.JLabel(); + + setLayout(new java.awt.CardLayout(5, 5)); + + outlineView.setPreferredSize(new java.awt.Dimension(300, 400)); + add(outlineView, "outlineCard"); + + messagePanel.setLayout(new java.awt.BorderLayout()); + + messageLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + org.openide.awt.Mnemonics.setLocalizedText(messageLabel, org.openide.util.NbBundle.getMessage(OutlineViewPanel.class, "OutlineViewPanel.messageLabel.text")); // NOI18N + messageLabel.setEnabled(false); + messagePanel.add(messageLabel, java.awt.BorderLayout.CENTER); + + add(messagePanel, "messageCard"); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel messageLabel; + private javax.swing.JPanel messagePanel; + private org.openide.explorer.view.OutlineView outlineView; + // End of variables declaration//GEN-END:variables + +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.form old mode 100644 new mode 100755 similarity index 52% rename from Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.form rename to Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.form index 58e19523f1..ac40867a25 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.form +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.form @@ -5,7 +5,7 @@ - + @@ -16,40 +16,36 @@ - - - + + + - - - - + + + - + - - - - + - + - - - - - - - + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.java new file mode 100755 index 0000000000..03729540df --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipBrowser.java @@ -0,0 +1,132 @@ +/* + * 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.awt.Component; +import javax.swing.JPanel; +import org.openide.util.Lookup; +import org.sleuthkit.autopsy.communications.ModifiableProxyLookup; + +/** + * Displays the Relationship information for the currently selected accounts. + * + */ +public final class RelationshipBrowser extends JPanel implements Lookup.Provider { + + private SelectionInfo currentSelection; + + private final MessagesViewer messagesViewer; + private final ContactsViewer contactsViewer; + private final SummaryViewer summaryViewer; + private final MediaViewer mediaViewer; + + private final ModifiableProxyLookup proxyLookup; + + /** + * Creates new form RelationshipBrowser + */ + public RelationshipBrowser() { + messagesViewer = new MessagesViewer(); + contactsViewer = new ContactsViewer(); + summaryViewer = new SummaryViewer(); + mediaViewer = new MediaViewer(); + + proxyLookup = new ModifiableProxyLookup(messagesViewer.getLookup()); + + initComponents(); + + tabPane.add(summaryViewer.getDisplayName(), summaryViewer); + tabPane.add(messagesViewer.getDisplayName(), messagesViewer); + tabPane.add(contactsViewer.getDisplayName(), contactsViewer); + tabPane.add(mediaViewer.getDisplayName(), mediaViewer); + + + } + + /** + * Sets the value of currentSelection and passes the SelectionInfo onto the + * currently selected\visible tab. + * + * @param info Currently selected account nodes + */ + public void setSelectionInfo(SelectionInfo info) { + currentSelection = info; + ((RelationshipsViewer) tabPane.getSelectedComponent()).setSelectionInfo(info); + } + + /** + * 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() { + + scrollPane = new javax.swing.JScrollPane(); + tabPane = new javax.swing.JTabbedPane(); + + scrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); + + tabPane.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + tabPaneStateChanged(evt); + } + }); + scrollPane.setViewportView(tabPane); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 400, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(scrollPane, javax.swing.GroupLayout.Alignment.TRAILING)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 300, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(scrollPane)) + ); + }// //GEN-END:initComponents + + private void tabPaneStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_tabPaneStateChanged + if(currentSelection != null) { + ((RelationshipsViewer) tabPane.getSelectedComponent()).setSelectionInfo(currentSelection); + } + + Component selectedComponent = tabPane.getSelectedComponent(); + if(selectedComponent instanceof Lookup.Provider) { + Lookup lookup = ((Lookup.Provider)selectedComponent).getLookup(); + proxyLookup.setNewLookups(lookup); + } + }//GEN-LAST:event_tabPaneStateChanged + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JScrollPane scrollPane; + private javax.swing.JTabbedPane tabPane; + // End of variables declaration//GEN-END:variables + + @Override + public Lookup getLookup() { + return proxyLookup; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsViewer.java new file mode 100755 index 0000000000..25f5c83701 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsViewer.java @@ -0,0 +1,50 @@ +/* + * 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 javax.swing.JPanel; +import org.openide.util.Lookup; + +/** + * Interface for Controls wishing to appear in the RelationshipBrowser tabPane. + */ +public interface RelationshipsViewer extends Lookup.Provider { + + /** + * Returns the value to be displayed on the "tab" + * + * @return String display name + */ + public String getDisplayName(); + + /** + * Returns the JPanel to be displayed in the RelationshipBrowser. + * + * @return JPanel to be displayed + */ + public JPanel getPanel(); + + /** + * Sets current SelectionInfo allowing the panel to update accordingly. + * + * @param info SelectionInfo instance representing the currently selected + * accounts + */ + public void setSelectionInfo(SelectionInfo info); +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SelectionInfo.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/SelectionInfo.java new file mode 100755 index 0000000000..16c92b3b94 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SelectionInfo.java @@ -0,0 +1,185 @@ +/* + * 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.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.datamodel.Account; +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; + +/** + * Class to wrap the details of the current selection from the AccountBrowser or + * VisualizationPane + */ +public final class SelectionInfo { + + private static final Logger logger = Logger.getLogger(SelectionInfo.class.getName()); + + private final Set accountDeviceInstances; + private final CommunicationsFilter communicationFilter; + private final Set accounts; + + private Set accountArtifacts = null; + private SelectionSummary summary = null; + + /** + * Wraps the details of the currently selected accounts. + * + * @param accountDeviceInstances Selected accountDecivedInstances + * @param communicationFilter Currently selected communications filters + */ + public SelectionInfo(Set accountDeviceInstances, CommunicationsFilter communicationFilter) { + this.accountDeviceInstances = accountDeviceInstances; + this.communicationFilter = communicationFilter; + + accounts = new HashSet<>(); + accountDeviceInstances.forEach((instance) -> { + accounts.add(instance.getAccount()); + }); + } + + /** + * Returns the currently selected accountDeviceInstances + * + * @return Set of AccountDeviceInstance + */ + public Set getAccountDevicesInstances() { + return accountDeviceInstances; + } + + /** + * Returns the currently selected communications filters. + * + * @return Instance of CommunicationsFilter + */ + public CommunicationsFilter getCommunicationsFilter() { + return communicationFilter; + } + + public Set getAccounts() { + return accounts; + } + + public Set getArtifacts() { + if(accountArtifacts == null) { + accountArtifacts = new HashSet<>(); + CommunicationsManager communicationManager; + try { + communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager(); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.SEVERE, "Failed to get communications manager from case.", ex); //NON-NLS + return null; + } + + final Set relationshipSources; + + try { + relationshipSources = communicationManager.getRelationshipSources(getAccountDevicesInstances(), getCommunicationsFilter()); + + relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> { + accountArtifacts.add((BlackboardArtifact) content); + }); + + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Failed to get relationship sources.", ex); //NON-NLS + } + } + + return accountArtifacts; + } + + public SelectionSummary getSummary() { + if(summary == null) { + summary = new SelectionSummary(); + } + + return summary; + } + + final class SelectionSummary{ + int attachmentCnt; + int messagesCnt; + int emailCnt; + int callLogCnt; + int contactsCnt; + + SelectionSummary() { + getCounts(); + } + + private void getCounts(){ + for(BlackboardArtifact artifact: getArtifacts()) { + BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); + if(null != fromID) switch (fromID) { + case TSK_EMAIL_MSG: + emailCnt++; + break; + case TSK_CALLLOG: + callLogCnt++; + break; + case TSK_MESSAGE: + messagesCnt++; + break; + case TSK_CONTACT: + contactsCnt++; + break; + default: + break; + } + try{ + attachmentCnt+= artifact.getChildrenCount(); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Exception thrown " + + "from getChildrenCount artifactID: %d", + artifact.getArtifactID()), ex); //NON-NLS + } + } + } + + public int getAttachmentCnt() { + return attachmentCnt; + } + + public int getMessagesCnt() { + return messagesCnt; + } + + public int getEmailCnt() { + return emailCnt; + } + + public int getCallLogCnt() { + return callLogCnt; + } + + public int getContactsCnt() { + return contactsCnt; + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.form new file mode 100755 index 0000000000..af02a7dee7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.form @@ -0,0 +1,215 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java new file mode 100755 index 0000000000..5641fad521 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java @@ -0,0 +1,313 @@ +/* + * 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.Set; +import javax.swing.JPanel; +import org.netbeans.swing.outline.DefaultOutlineModel; +import org.netbeans.swing.outline.Outline; +import org.openide.explorer.view.OutlineView; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; +import org.openide.util.Lookup; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.communications.relationships.SelectionInfo.SelectionSummary; +import org.sleuthkit.datamodel.Account; + +/** + * Account Summary View Panel. This panel shows a list of various counts related + * to the currently selected account. As well has a panel showing a list of + * cases and files that reference the account. + * + */ +public class SummaryViewer extends javax.swing.JPanel implements RelationshipsViewer { + + private final Lookup lookup; + + @Messages({ + "SummaryViewer_TabTitle=Summary", + "SummaryViewer_FileRefNameColumn_Title=Path", + "SummaryViewer_CaseRefNameColumn_Title=Case Name", + "SummaryViewer_CentralRepository_Message=", + "SummaryViewer_Creation_Date_Title=Creation Date" + }) + + /** + * Creates new form SummaryViewer + */ + public SummaryViewer() { + lookup = Lookup.getDefault(); + initComponents(); + + OutlineView outlineView = fileReferencesPanel.getOutlineView(); + Outline outline = outlineView.getOutline(); + + outline.setRootVisible(false); + ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.SummaryViewer_FileRefNameColumn_Title()); + + outlineView = caseReferencesPanel.getOutlineView(); + outline = outlineView.getOutline(); + outlineView.setPropertyColumns("creationDate", Bundle.SummaryViewer_Creation_Date_Title()); //NON-NLS + + outline.setRootVisible(false); + ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.SummaryViewer_CaseRefNameColumn_Title()); + + clearControls(); + } + + @Override + public String getDisplayName() { + return Bundle.SummaryViewer_TabTitle(); + } + + @Override + public JPanel getPanel() { + return this; + } + + @Override + public void setSelectionInfo(SelectionInfo info) { + + if (!EamDb.isEnabled()) { + caseReferencesPanel.hideOutlineView(Bundle.SummaryViewer_CentralRepository_Message()); + } else { + caseReferencesPanel.showOutlineView(); + } + + // Request is that the SummaryViewer only show information if one + // account is selected + if (info.getAccounts().size() != 1) { + setEnabled(false); + clearControls(); + + } else { + SelectionSummary summaryDetails = info.getSummary(); + + attachmentsDataLabel.setText(Integer.toString(summaryDetails.getAttachmentCnt())); + callLogsDataLabel.setText(Integer.toString(summaryDetails.getCallLogCnt())); + contactsDataLabel.setText(Integer.toString(summaryDetails.getContactsCnt())); + emailDataLabel.setText(Integer.toString(summaryDetails.getEmailCnt())); + messagesDataLabel.setText(Integer.toString(summaryDetails.getMessagesCnt())); + + fileReferencesPanel.setNode(new AbstractNode(Children.create(new AccountSourceContentChildNodeFactory(info.getAccounts()), true))); + caseReferencesPanel.setNode(new AbstractNode(Children.create(new CorrelationCaseChildNodeFactory(info.getAccounts()), true))); + + setEnabled(true); + } + } + + @Override + public Lookup getLookup() { + return lookup; + } + + /** + * Sets whether or not the text fields are enabled. + * + * @param enabled true if this component should be enabled, false otherwise + */ + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + attachmentsLabel.setEnabled(enabled); + callLogsLabel.setEnabled(enabled); + contactsLabel.setEnabled(enabled); + emailLabel.setEnabled(enabled); + messagesLabel.setEnabled(enabled); + caseReferencesPanel.setEnabled(enabled); + fileReferencesPanel.setEnabled(enabled); + countsPanel.setEnabled(enabled); + } + + /** + * Clears the text fields and OutlookViews. + */ + private void clearControls() { + attachmentsDataLabel.setText(""); + callLogsDataLabel.setText(""); + contactsDataLabel.setText(""); + emailDataLabel.setText(""); + messagesDataLabel.setText(""); + + fileReferencesPanel.setNode(new AbstractNode(Children.LEAF)); + caseReferencesPanel.setNode(new AbstractNode(Children.LEAF)); + } + + /** + * For the given accounts create a comma separated string of all of the + * names (TypeSpecificID). + * + * @param accounts Set of selected accounts + * + * @return String listing the account names + */ + private String createAccountLabel(Set accounts) { + StringBuilder buffer = new StringBuilder(); + accounts.stream().map((account) -> { + buffer.append(account.getTypeSpecificID()); + return account; + }).forEachOrdered((_item) -> { + buffer.append(", "); + }); + + return buffer.toString().substring(0, buffer.length() - 2); + } + + /** + * 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() { + + countsPanel = new javax.swing.JPanel(); + emailLabel = new javax.swing.JLabel(); + contactsLabel = new javax.swing.JLabel(); + messagesLabel = new javax.swing.JLabel(); + callLogsLabel = new javax.swing.JLabel(); + attachmentsLabel = new javax.swing.JLabel(); + attachmentsDataLabel = new javax.swing.JLabel(); + messagesDataLabel = new javax.swing.JLabel(); + callLogsDataLabel = new javax.swing.JLabel(); + contactsDataLabel = new javax.swing.JLabel(); + emailDataLabel = new javax.swing.JLabel(); + fileReferencesPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel(); + caseReferencesPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel(); + + countsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.countsPanel.border.title"))); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(emailLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.emailLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(contactsLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.contactsLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(messagesLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.messagesLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(callLogsLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.callLogsLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(attachmentsLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.attachmentsLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(attachmentsDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.attachmentsDataLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(messagesDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.messagesDataLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(callLogsDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.callLogsDataLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(contactsDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.contactsDataLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(emailDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.emailDataLabel.text")); // NOI18N + + javax.swing.GroupLayout countsPanelLayout = new javax.swing.GroupLayout(countsPanel); + countsPanel.setLayout(countsPanelLayout); + countsPanelLayout.setHorizontalGroup( + countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(countsPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(attachmentsLabel) + .addComponent(messagesLabel) + .addComponent(callLogsLabel) + .addComponent(contactsLabel) + .addComponent(emailLabel)) + .addGap(18, 18, 18) + .addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(emailDataLabel) + .addComponent(contactsDataLabel) + .addComponent(callLogsDataLabel) + .addComponent(messagesDataLabel) + .addComponent(attachmentsDataLabel)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + countsPanelLayout.setVerticalGroup( + countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(countsPanelLayout.createSequentialGroup() + .addGap(7, 7, 7) + .addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(attachmentsLabel) + .addComponent(attachmentsDataLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(messagesLabel) + .addComponent(messagesDataLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(callLogsLabel) + .addComponent(callLogsDataLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(contactsLabel) + .addComponent(contactsDataLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(emailLabel) + .addComponent(emailDataLabel)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + fileReferencesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.fileReferencesPanel.border.title"))); // NOI18N + fileReferencesPanel.setPreferredSize(new java.awt.Dimension(472, 300)); + + caseReferencesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.caseReferencesPanel.border.title"))); // NOI18N + caseReferencesPanel.setPreferredSize(new java.awt.Dimension(472, 300)); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(countsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(fileReferencesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 485, Short.MAX_VALUE) + .addComponent(caseReferencesPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(countsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(fileReferencesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(caseReferencesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel attachmentsDataLabel; + private javax.swing.JLabel attachmentsLabel; + private javax.swing.JLabel callLogsDataLabel; + private javax.swing.JLabel callLogsLabel; + private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel caseReferencesPanel; + private javax.swing.JLabel contactsDataLabel; + private javax.swing.JLabel contactsLabel; + private javax.swing.JPanel countsPanel; + private javax.swing.JLabel emailDataLabel; + private javax.swing.JLabel emailLabel; + private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel fileReferencesPanel; + private javax.swing.JLabel messagesDataLabel; + private javax.swing.JLabel messagesLabel; + // End of variables declaration//GEN-END:variables + +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index 236fddfada..a48a03300b 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -147,7 +147,7 @@ MediaViewImagePanel.rotationTextField.text= MediaViewImagePanel.rotateLeftButton.toolTipText= HtmlPanel.showImagesToggleButton.text=Show Images MediaPlayerPanel.audioSlider.toolTipText= -MediaPlayerPanel.VolumeIcon.text=\ \ \ \ \ Volume +MediaPlayerPanel.VolumeIcon.text=\ Volume MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00 MediaPlayerPanel.playButton.text=\u25ba MediaPlayerPanel.infoLabel.text=No Errors diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java index 201f3f1f6f..b2d4baef99 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org *s * Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,6 +36,7 @@ import org.sleuthkit.datamodel.AbstractFile; class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer { private static final Logger LOGGER = Logger.getLogger(MediaFileViewer.class.getName()); + private static final long serialVersionUID = 1L; private AbstractFile lastFile; //UI private MediaPlayerPanel mediaPlayerPanel; @@ -48,7 +49,7 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer { /** * Creates a new MediaFileViewer. */ - public MediaFileViewer() { + MediaFileViewer() { initComponents(); @@ -69,8 +70,8 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer { private void customizeComponents() { add(imagePanel, IMAGE_VIEWER_LAYER); - - if(mediaPlayerPanel != null) { + + if (mediaPlayerPanel != null) { add(mediaPlayerPanel, MEDIA_PLAYER_LAYER); } @@ -103,10 +104,10 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer { List mimeTypes = new ArrayList<>(); mimeTypes.addAll(this.imagePanel.getSupportedMimeTypes()); - if(mediaPlayerPanel != null) { + if (mediaPlayerPanel != null) { mimeTypes.addAll(this.mediaPlayerPanel.getSupportedMimeTypes()); } - + return mimeTypes; } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index 936716fc78..5c16dbed93 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -49,6 +49,10 @@ import javax.swing.JPanel; import org.controlsfx.control.MaskerPane; import org.openide.util.NbBundle; import org.python.google.common.collect.Lists; +import javafx.scene.Group; +import javafx.scene.input.MouseEvent; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.datamodel.FileNode; @@ -70,18 +74,20 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private final boolean fxInited; private JFXPanel fxPanel; + private Group imageGroup; + private ImageTaggingTool tagger; private ImageView fxImageView; private ScrollPane scrollPane; private final ProgressBar progressBar = new ProgressBar(); private final MaskerPane maskerPane = new MaskerPane(); - + private double zoomRatio; private double rotation; // Can be 0, 90, 180, and 270. - + private static final double[] ZOOM_STEPS = { 0.0625, 0.125, 0.25, 0.375, 0.5, 0.75, 1, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10}; - + private static final double MIN_ZOOM_RATIO = 0.0625; // 6.25% private static final double MAX_ZOOM_RATIO = 10.0; // 1000% @@ -115,11 +121,13 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan // build jfx ui (we could do this in FXML?) fxImageView = new ImageView(); // will hold image - scrollPane = new ScrollPane(fxImageView); // scrolls and sizes imageview + imageGroup = new Group(); + imageGroup.getChildren().add(fxImageView); + scrollPane = new ScrollPane(imageGroup); // scrolls and sizes imageview scrollPane.getStyleClass().add("bg"); //NOI18N scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED); scrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED); - + fxPanel = new JFXPanel(); // bridge jfx-swing Scene scene = new Scene(scrollPane); //root of jfx tree scene.getStylesheets().add(MediaViewImagePanel.class.getResource("MediaViewImagePanel.css").toExternalForm()); //NOI18N @@ -146,9 +154,10 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan Platform.runLater(() -> { fxImageView.setViewport(new Rectangle2D(0, 0, 0, 0)); fxImageView.setImage(null); - + tagger.defaultSettings(); + scrollPane.setContent(null); - scrollPane.setContent(fxImageView); + scrollPane.setContent(imageGroup); }); } @@ -160,7 +169,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan * TODO: why is the name passed into the action constructor? it * means we duplicate this string all over the place -jm */ new ExternalViewerAction(Bundle.MediaViewImagePanel_externalViewerButton_text(), new FileNode(file)) - .actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "")) //Swing ActionEvent + .actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "")) //Swing ActionEvent ); final VBox errorNode = new VBox(10, new Label(errorMessage), externalViewerButton); @@ -199,8 +208,11 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan if (nonNull(fxImage)) { // We have a non-null image, so let's show it. fxImageView.setImage(fxImage); + imageGroup.getChildren().remove(tagger); + tagger = new ImageTaggingTool(fxImageView, Color.RED); + imageGroup.getChildren().add(tagger); resetView(); - scrollPane.setContent(fxImageView); + scrollPane.setContent(imageGroup); } else { showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file); } @@ -410,7 +422,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private void zoomInButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed // Find the next zoom step. - for (int i=0; i < ZOOM_STEPS.length; i++) { + for (int i = 0; i < ZOOM_STEPS.length; i++) { if (zoomRatio < ZOOM_STEPS[i]) { zoomRatio = ZOOM_STEPS[i]; break; @@ -421,7 +433,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private void zoomOutButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed // Find the next zoom step. - for (int i=ZOOM_STEPS.length-1; i >= 0; i--) { + for (int i = ZOOM_STEPS.length - 1; i >= 0; i--) { if (zoomRatio > ZOOM_STEPS[i]) { zoomRatio = ZOOM_STEPS[i]; break; @@ -450,12 +462,12 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan private javax.swing.JButton zoomResetButton; private javax.swing.JTextField zoomTextField; // End of variables declaration//GEN-END:variables - + /** * Reset the zoom and rotation values to their defaults. The zoom level gets * defaulted to the current size of the panel. The rotation will be set to * zero. - * + * * Note: This method will make a call to 'updateView()' after the values * have been reset. */ @@ -464,28 +476,28 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan if (image == null) { return; } - + double imageWidth = image.getWidth(); double imageHeight = image.getHeight(); double scrollPaneWidth = fxPanel.getWidth(); double scrollPaneHeight = fxPanel.getHeight(); double zoomRatioWidth = scrollPaneWidth / imageWidth; double zoomRatioHeight = scrollPaneHeight / imageHeight; - + // Use the smallest ratio size to fit the entire image in the view area. zoomRatio = zoomRatioWidth < zoomRatioHeight ? zoomRatioWidth : zoomRatioHeight; - + rotation = 0; - + scrollPane.setHvalue(0); scrollPane.setVvalue(0); - + updateView(); } - + /** * Update the image to use the current zoom and rotation values. - * + * * Note: For zoom levels less than 100%, special accomodations are made in * order to keep the image fully visible. This is because the viewport size * change occurs before any transforms execute, thus chopping off part of @@ -498,39 +510,39 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan if (image == null) { return; } - + // Image dimensions double imageWidth = image.getWidth(); double imageHeight = image.getHeight(); - + // Image dimensions with zooming applied double adjustedImageWidth = imageWidth * zoomRatio; double adjustedImageHeight = imageHeight * zoomRatio; - + // ImageView viewport dimensions double viewportWidth; double viewportHeight; - + // Panel dimensions double panelWidth = fxPanel.getWidth(); double panelHeight = fxPanel.getHeight(); - + // Coordinates to center the image on the panel double centerOffsetX = (panelWidth / 2) - (imageWidth / 2); double centerOffsetY = (panelHeight / 2) - (imageHeight / 2); - + // Coordinates to keep the image inside the left/top boundaries double leftOffsetX; double topOffsetY; - + // Scroll bar positions double scrollX = scrollPane.getHvalue(); double scrollY = scrollPane.getVvalue(); - + // Scroll bar position boundaries (work-around for viewport size bug) double maxScrollX; double maxScrollY; - + // Set viewport size and translation offsets. if ((rotation % 180) == 0) { // Rotation is 0 or 180. @@ -549,7 +561,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan maxScrollX = (adjustedImageHeight - panelWidth) / (imageWidth - panelWidth); maxScrollY = (adjustedImageWidth - panelHeight) / (imageHeight - panelHeight); } - + // Work around bug that truncates image if dimensions are too small. if (viewportWidth < imageWidth) { viewportWidth = imageWidth; @@ -563,7 +575,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan scrollY = maxScrollY; } } - + // Update the viewport size. fxImageView.setViewport(new Rectangle2D( 0, 0, viewportWidth, viewportHeight)); @@ -589,9 +601,9 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan // Add the transforms in reverse order of intended execution. // Note: They MUST be added in this order to ensure translate is // executed last. - fxImageView.getTransforms().clear(); - fxImageView.getTransforms().addAll(translate, rotate, scale); - + imageGroup.getTransforms().clear(); + imageGroup.getTransforms().addAll(translate, rotate, scale); + // Adjust scroll bar positions for view changes. if (viewportWidth > fxPanel.getWidth()) { scrollPane.setHvalue(scrollX); @@ -599,11 +611,114 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan if (viewportHeight > fxPanel.getHeight()) { scrollPane.setVvalue(scrollY); } - + // Update all image controls to reflect the current values. zoomOutButton.setEnabled(zoomRatio > MIN_ZOOM_RATIO); zoomInButton.setEnabled(zoomRatio < MAX_ZOOM_RATIO); rotationTextField.setText((int) rotation + "°"); zoomTextField.setText((Math.round(zoomRatio * 100.0)) + "%"); } + + /** + * Enables users to 'tag' a region of an image by clicking and dragging a + * rectangle overtop. + */ + class ImageTaggingTool extends Rectangle { + + private final double imageWidth; + private final double imageHeight; + private final double imageOriginX; + private final double imageOriginY; + + //Origin of the drag event. + private double rectangleOriginX; + private double rectangleOriginY; + + //Rectangle lines should be 1.5% of the image. This level of thickness has + //a good balance between visual acuity and loss of selection at the borders + //of the image. + private double lineThicknessAsPercent = 1.5; + + /** + * Adds tagging support to an image, where the 'tag' rectangle will be + * the specified color. + * + * @param image Image to tag + * @param color Color of the 'tag' rectangle + */ + private ImageTaggingTool(ImageView image, Color color) { + defaultSettings(); + + imageWidth = image.getImage().getWidth(); + imageHeight = image.getImage().getHeight(); + imageOriginX = image.getX(); + imageOriginY = image.getY(); + + setStroke(color); + setFill(color.deriveColor(0, 0, 0, 0)); + + //Calculate how many pixels the stroke width should be to guarentee + //a consistent % of image consumed by the rectangle border. + double min = Math.min(imageWidth, imageHeight); + double lineThicknessPixels = min * lineThicknessAsPercent / 100.0; + setStrokeWidth(lineThicknessPixels); + setVisible(false); + + //Create a rectangle by left clicking on the image + image.setOnMousePressed((MouseEvent event) -> { + if (event.isSecondaryButtonDown()) { + return; + } + + //Reset box on new click. + defaultSettings(); + + rectangleOriginX = event.getX(); + rectangleOriginY = event.getY(); + + setX(rectangleOriginX); + setY(rectangleOriginY); + }); + + //Adjust the rectangle by dragging the left mouse button + image.setOnMouseDragged((MouseEvent event) -> { + if (event.isSecondaryButtonDown()) { + return; + } + + /** + * Ensure the rectangle is contained within image boundaries and + * that the line thickness is kept within bounds. + */ + double newX = Math.min(Math.max(event.getX(), imageOriginX) + + lineThicknessPixels / 2, imageWidth - lineThicknessPixels / 2); + double newY = Math.min(Math.max(event.getY(), imageOriginY) + + lineThicknessPixels / 2, imageHeight - lineThicknessPixels / 2); + + setVisible(true); + double offsetX = newX - rectangleOriginX; + if (offsetX < 0) { + setX(newX); + } + setWidth(Math.abs(offsetX)); + + double offsetY = newY - rectangleOriginY; + if (offsetY < 0) { + setY(newY); + } + setHeight(Math.abs(offsetY)); + }); + } + + /** + * Reset the rectangle to default dimensions. + */ + public final void defaultSettings() { + setX(0); + setY(0); + setWidth(0); + setHeight(0); + setVisible(false); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index 00c87b3417..48f7e4e82e 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -467,6 +467,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont htmlPanel.reset(); textbodyTextArea.setText(""); msgbodyTabbedPane.setEnabled(false); + drp.setNode(null); } @Override @@ -561,7 +562,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, numberOfAttachments > 0); msgbodyTabbedPane.setTitleAt(ATTM_TAB_INDEX, "Attachments (" + numberOfAttachments + ")"); drp.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode( - new AttachmentsChildren(attachments)), null), true)); + new AttachmentsChildren(attachments))), true)); } private static String wrapInHtmlBody(String htmlText) { diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java index dd73b6f6ca..c1fdda1efc 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2018 Basis Technology Corp. + * Copyright 2018-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -79,7 +79,7 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer /** * Creates new form PListViewer */ - public PListViewer() { + PListViewer() { // Create an Outlineview and add to the panel outlineView = new org.openide.explorer.view.OutlineView(); @@ -193,16 +193,16 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer Case openCase; try { openCase = Case.getCurrentCaseThrows(); - } catch (NoCurrentCaseException ex) { - JOptionPane.showMessageDialog(this, - "Failed to export plist file.", - Bundle.PListViewer_ExportFailed_message(), - JOptionPane.ERROR_MESSAGE); + } catch (NoCurrentCaseException ex) { + JOptionPane.showMessageDialog(this, + "Failed to export plist file.", + Bundle.PListViewer_ExportFailed_message(), + JOptionPane.ERROR_MESSAGE); - logger.log(Level.SEVERE, "Exception while getting open case.", ex); - return; + logger.log(Level.SEVERE, "Exception while getting open case.", ex); + return; } - + final JFileChooser fileChooser = new JFileChooser(); fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); fileChooser.setFileFilter(new FileNameExtensionFilter("XML file", "xml")); @@ -289,11 +289,11 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer // Read in and parse the file final byte[] plistFileBuf = new byte[(int) plistFile.getSize()]; plistFile.read(plistFileBuf, 0, plistFile.getSize()); - final List plist = parsePList(plistFileBuf); - + final List plist = parsePList(plistFileBuf); + return plist; } - + @Override protected void done() { super.done(); @@ -301,28 +301,28 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer try { plist = get(); setupTable(plist); - + SwingUtilities.invokeLater(() -> { - setColumnWidths(); + setColumnWidths(); }); } catch (InterruptedException ex) { logger.log(Level.SEVERE, "Interruption while parsing/dislaying plist file " + plistFile.getName(), ex); - - JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), - ex.getMessage(), - Bundle.PListViewer_processPlist_interruptedMessage(), - JOptionPane.ERROR_MESSAGE); - + + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + ex.getMessage(), + Bundle.PListViewer_processPlist_interruptedMessage(), + JOptionPane.ERROR_MESSAGE); + } catch (ExecutionException ex) { logger.log(Level.SEVERE, "Exception while parsing/dislaying plist file " + plistFile.getName(), ex); - JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), - ex.getCause().getMessage(), - Bundle.PListViewer_processPlist_errorMessage(), - JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + ex.getCause().getMessage(), + Bundle.PListViewer_processPlist_errorMessage(), + JOptionPane.ERROR_MESSAGE); } - + } - }.execute(); + }.execute(); } /** @@ -431,16 +431,16 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer * else is unexpected and will be ignored. */ if (rootDict instanceof NSArray) { - for (int i=0; i < ((NSArray)rootDict).count(); i++) { - final PropKeyValue pkv = parseProperty("", ((NSArray)rootDict).objectAtIndex(i)); + for (int i = 0; i < ((NSArray) rootDict).count(); i++) { + final PropKeyValue pkv = parseProperty("", ((NSArray) rootDict).objectAtIndex(i)); if (null != pkv) { plist.add(pkv); } } } else if (rootDict instanceof NSDictionary) { - final String[] keys = ((NSDictionary)rootDict).allKeys(); + final String[] keys = ((NSDictionary) rootDict).allKeys(); for (final String key : keys) { - final PropKeyValue pkv = parseProperty(key, ((NSDictionary)rootDict).objectForKey(key)); + final PropKeyValue pkv = parseProperty(key, ((NSDictionary) rootDict).objectForKey(key)); if (null != pkv) { plist.add(pkv); } @@ -533,7 +533,7 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer .map(child -> new PropKeyValue(child)) .toArray(PropKeyValue[]::new); } - + } } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index 104c70ef6b..078cc78c58 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2018 Basis Technology Corp. + * Copyright 2018-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -74,7 +74,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { /** * Constructs a file content viewer for SQLite database files. */ - public SQLiteViewer() { + SQLiteViewer() { initComponents(); jTableDataPanel.add(selectedTableView, BorderLayout.CENTER); } @@ -544,19 +544,19 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { @Override public void accept(String columnName) { columnIndex++; - + String csvString = columnName; //Format the value to adhere to the format of a CSV file if (columnIndex == 1) { - columnName = "\"" + columnName + "\""; + csvString = "\"" + csvString + "\""; } else { - columnName = ",\"" + columnName + "\""; + csvString = ",\"" + csvString + "\""; } if (columnIndex == totalColumnCount) { - columnName += "\n"; + csvString += "\n"; } try { - out.write(columnName.getBytes()); + out.write(csvString.getBytes()); } catch (IOException ex) { /* * If we can no longer write to the output stream, toss a @@ -613,7 +613,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { */ throw new RuntimeException(ex); } - rowIndex = rowIndex % totalColumnCount; + rowIndex %= totalColumnCount; } }; } diff --git a/Core/src/org/sleuthkit/autopsy/core/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/core/Bundle.properties-MERGED index 087eaec314..f7039ab4ce 100755 --- a/Core/src/org/sleuthkit/autopsy/core/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/core/Bundle.properties-MERGED @@ -3,13 +3,7 @@ Installer.closing.confirmationDialog.title=Ingest is Running # {0} - exception message Installer.closing.messageBox.caseCloseExceptionMessage=Error closing case: {0} OpenIDE-Module-Display-Category=Infrastructure -OpenIDE-Module-Long-Description=\ - This is the core Autopsy module.\n\n\ - The module contains the core components needed for the bare application to run; the RCP platform, windowing GUI, sleuthkit bindings, datamodel / storage, explorer, result viewers, content viewers, ingest framework, reporting, and core tools, such as the file search.\n\n\ - The framework included in the module contains APIs for developing modules for ingest, viewers and reporting. \ - The modules can be deployed as Plugins using the Autopsy plugin installer.\n\ - This module should not be uninstalled - without it, Autopsy will not run.\n\n\ - For more information, see http://www.sleuthkit.org/autopsy/ +OpenIDE-Module-Long-Description=This is the core Autopsy module.\n\nThe module contains the core components needed for the bare application to run; the RCP platform, windowing GUI, sleuthkit bindings, datamodel / storage, explorer, result viewers, content viewers, ingest framework, reporting, and core tools, such as the file search.\n\nThe framework included in the module contains APIs for developing modules for ingest, viewers and reporting. The modules can be deployed as Plugins using the Autopsy plugin installer.\nThis module should not be uninstalled - without it, Autopsy will not run.\n\nFor more information, see http://www.sleuthkit.org/autopsy/ OpenIDE-Module-Name=Autopsy-Core OpenIDE-Module-Short-Description=Autopsy Core Module org_sleuthkit_autopsy_core_update_center=http://sleuthkit.org/autopsy/updates.xml diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED index afe2fb7724..b56a9c28f6 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED @@ -56,9 +56,9 @@ DataContentViewerHex.totalPageLabel.text_1=100 DataContentViewerHex.pageLabel2.text=Page # Product Information panel -LBL_Description=
\n Product Version: {0} ({9})
Sleuth Kit Version: {7}
Netbeans RCP Build: {8}
Java: {1}; {2}
System: {3}; {4}; {5}
Userdir: {6}
+LBL_Description=
\n Product Version: {0} ({9})
Sleuth Kit Version: {7}
Netbeans RCP Build: {8}
Java: {1}; {2}
System: {3}; {4}; {5}
Userdir: {6}
Format_OperatingSystem_Value={0} version {1} running on {2} -LBL_Copyright=
Autopsy™ is a digital forensics platform based on The Sleuth Kit™ and other tools.
Copyright © 2003-2018.
+LBL_Copyright=
Autopsy™ is a digital forensics platform based on The Sleuth Kit™ and other tools.
Copyright © 2003-2018.
SortChooser.dialogTitle=Choose Sort Criteria ThumbnailViewChildren.progress.cancelling=(Cancelling) # {0} - file name @@ -88,7 +88,7 @@ DataResultViewerThumbnail.pageNextButton.text= DataResultViewerThumbnail.imagesLabel.text=Images: DataResultViewerThumbnail.imagesRangeLabel.text=- DataResultViewerThumbnail.pageNumLabel.text=- -DataResultViewerThumbnail.filePathLabel.text=\ \ \ +DataResultViewerThumbnail.filePathLabel.text=\ DataResultViewerThumbnail.goToPageLabel.text=Go to Page: DataResultViewerThumbnail.goToPageField.text= AdvancedConfigurationDialog.cancelButton.text=Cancel diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java index f03c7b01cf..4f4e41d04e 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java @@ -386,7 +386,13 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { rootNodeChildren.cancelLoadingThumbnails(); } try { - if (givenNode != null) { + // There is an issue with ThumbnailViewChildren + // addNotify, that it's call to getChildren.getNodes() does not cause the + // children nodes to be created. Adding a call to getChildren.getNodesCount() + // here will assure that the children nodes are created particularly in the + // case where the DataResultViewerThumbnail stands along from the + // DataResultViewer. See DataResultViewer setNode for more information. + if (givenNode != null && givenNode.getChildren().getNodesCount() > 0) { rootNode = (TableFilterNode) givenNode; /* * Wrap the given node in a ThumbnailViewChildren that will diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/coreutils/Bundle.properties-MERGED index 17791d159d..702b726e08 100755 --- a/Core/src/org/sleuthkit/autopsy/coreutils/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/coreutils/Bundle.properties-MERGED @@ -23,9 +23,7 @@ PlatformUtil.getProcVmUsed.sigarNotInit.msg=Cannot get virt mem used, sigar not PlatformUtil.getProcVmUsed.gen.msg=Cannot get virt mem used, {0} PlatformUtil.getJvmMemInfo.usageText=JVM heap usage: {0}, JVM non-heap usage: {1} PlatformUtil.getPhysicalMemInfo.usageText=Physical memory usage (max, total, free): {0}, {1}, {2} -PlatformUtil.getAllMemUsageInfo.usageText={0}\n\ -{1}\n\ -Process Virtual Memory: {2} +PlatformUtil.getAllMemUsageInfo.usageText={0}\n{1}\nProcess Virtual Memory: {2} # {0} - file name ReadImageTask.mesageText=Reading image: {0} StringExtract.illegalStateException.cannotInit.msg=Unicode table not properly initialized, cannot instantiate StringExtract diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED index c11a681494..2998a198a0 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED @@ -258,10 +258,10 @@ ImageNode.getActions.viewInNewWin.text=View in New Window ImageNode.createSheet.name.name=Name ImageNode.createSheet.name.displayName=Name ImageNode.createSheet.name.desc=no description -Installer.exception.tskVerStringNull.msg=Sleuth Kit JNI test call returned without error, but version string was null\! -Installer.exception.taskVerStringBang.msg=Sleuth Kit JNI test call returned without error, but version string was ""\! -Installer.tskLibErr.msg=Problem with Sleuth Kit JNI. Test call failed\!\n\nDetails: {0} -Installer.tskLibErr.err=Fatal Error\! +Installer.exception.tskVerStringNull.msg=Sleuth Kit JNI test call returned without error, but version string was null! +Installer.exception.taskVerStringBang.msg=Sleuth Kit JNI test call returned without error, but version string was ""! +Installer.tskLibErr.msg=Problem with Sleuth Kit JNI. Test call failed!\n\nDetails: {0} +Installer.tskLibErr.err=Fatal Error! InterestingHits.interestingItems.text=INTERESTING ITEMS InterestingHits.displayName.text=Interesting Items InterestingHits.createSheet.name.name=Name diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index c375c7f02b..d98e58fe70 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -126,7 +126,20 @@ public class DataResultFilterNode extends FilterNode { static private final DisplayableItemNodeVisitor> getActionsDIV = new GetPopupActionsDisplayableItemNodeVisitor(); private final DisplayableItemNodeVisitor getPreferredActionsDIV = new GetPreferredActionsDisplayableItemNodeVisitor(); + // Assumptions are made in GetPreferredActionsDisplayableItemNodeVisitor that + // sourceEm is the directory tree explorer manager. private final ExplorerManager sourceEm; + + /** + * Constructs a node used to wrap another node before passing it to the + * result viewers. The wrapper node defines the actions associated with the + * wrapped node and may filter out some of its children. + * + * @param node The node to wrap. + */ + public DataResultFilterNode(Node node) { + this(node, null); + } /** * Constructs a node used to wrap another node before passing it to the @@ -635,6 +648,9 @@ public class DataResultFilterNode extends FilterNode { // is a DirectoryTreeFilterNode that wraps the dataModelNode. We need // to set that wrapped node as the selection and root context of the // directory tree explorer manager (sourceEm) + if(sourceEm == null) { + return null; + } final Node currentSelectionInDirectoryTree = sourceEm.getSelectedNodes()[0]; return new AbstractAction() { @@ -675,6 +691,9 @@ public class DataResultFilterNode extends FilterNode { * @return */ private AbstractAction openParent(AbstractNode node) { + if(sourceEm == null) { + return null; + } // @@@ Why do we ignore node? Node[] selectedFilterNodes = sourceEm.getSelectedNodes(); Node selectedFilterNode = selectedFilterNodes[0]; diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties-MERGED index 08cc69c39c..7ab8ecbe04 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/filesearch/Bundle.properties-MERGED @@ -14,7 +14,7 @@ KnownStatusSearchPanel.knownCheckBox.text=Known Status: KnownStatusSearchPanel.knownBadOptionCheckBox.text=Notable KnownStatusSearchPanel.knownOptionCheckBox.text=Known (NSRL or other) KnownStatusSearchPanel.unknownOptionCheckBox.text=Unknown -DateSearchFilter.noneSelectedMsg.text=At least one date type must be selected\! +DateSearchFilter.noneSelectedMsg.text=At least one date type must be selected! DateSearchPanel.dateCheckBox.text=Date: DateSearchPanel.jLabel4.text=Timezone: DateSearchPanel.jLabel3.text=*The date format is mm/dd/yyyy @@ -56,7 +56,7 @@ FileSearchPanel.search.results.details=Large number of matches may impact perfor FileSearchPanel.search.exception.noFilterSelected.msg=At least one filter must be selected. FileSearchPanel.search.validationErr.msg=Validation Error: {0} FileSearchPanel.emptyWhereClause.text=Invalid options, nothing to show. -KnownStatusSearchFilter.noneSelectedMsg.text=At least one known status must be selected\! +KnownStatusSearchFilter.noneSelectedMsg.text=At least one known status must be selected! NameSearchFilter.emptyNameMsg.text=Must enter something for name search. SearchNode.getName.text=Search Result SizeSearchPanel.sizeCompareComboBox.equalTo=equal to diff --git a/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties-MERGED index 9e4f612b6b..6be3e48e71 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/ingest/Bundle.properties-MERGED @@ -140,7 +140,7 @@ IngestJob.cancelReason.outOfDiskSpace.text=Out of disk space IngestJob.cancelReason.servicesDown.text=Services Down IngestJob.cancelReason.caseClosed.text=Case closed IngestJobSettingsPanel.globalSettingsButton.text=Global Settings -gest +gest= IngestJobSettingsPanel.globalSettingsButton.actionCommand=Advanced IngestJobSettingsPanel.globalSettingsButton.text=Global Settings IngestJobSettingsPanel.pastJobsButton.text=History diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties-MERGED index d73865ac3e..4729293fb9 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties-MERGED @@ -11,12 +11,7 @@ ExtractArchiveWithPasswordAction.progress.text=Unpacking contents of archive: {0 ExtractArchiveWithPasswordAction.prompt.text=Enter Password ExtractArchiveWithPasswordAction.prompt.title=Enter Password OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=\ - Embedded File Extraction Ingest Module\n\nThe Embedded File Extraction Ingest Module processes document files (such as doc, docx, ppt, pptx, xls, xlsx) and archive files (such as zip and others archive types supported by the 7zip extractor).\n\ - Contents of these files are extracted and the derived files are added back to the current ingest to be processed by the configured ingest modules.\n\ - If the derived file happens to be an archive file, it will be re-processed by the 7zip extractor - the extractor will process archive files N-levels deep.\n\n\ - The extracted files are navigable in the directory tree.\n\n\ - The module is supported on Windows, Linux and Mac operating systems. +OpenIDE-Module-Long-Description=Embedded File Extraction Ingest Module\n\nThe Embedded File Extraction Ingest Module processes document files (such as doc, docx, ppt, pptx, xls, xlsx) and archive files (such as zip and others archive types supported by the 7zip extractor).\nContents of these files are extracted and the derived files are added back to the current ingest to be processed by the configured ingest modules.\nIf the derived file happens to be an archive file, it will be re-processed by the 7zip extractor - the extractor will process archive files N-levels deep.\n\nThe extracted files are navigable in the directory tree.\n\nThe module is supported on Windows, Linux and Mac operating systems. OpenIDE-Module-Name=Embedded File Extraction OpenIDE-Module-Short-Description=Embedded File Extraction Ingest Module EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.seek.exception.invalidOrigin=Invalid seek origin: {0} diff --git a/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED index 9905159d99..ee788daf61 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED @@ -1,9 +1,7 @@ CannotRunFileTypeDetection=Cannot run file type detection. ExifParserFileIngestModule.indexError.message=Failed to index EXIF Metadata artifact for keyword search. OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=\ - Exif metadata ingest module. \n\n\ - The ingest module analyzes image files, extracts Exif information and posts the Exif data as results. +OpenIDE-Module-Long-Description=Exif metadata ingest module. \n\nThe ingest module analyzes image files, extracts Exif information and posts the Exif data as results. OpenIDE-Module-Name=ExifParser OpenIDE-Module-Short-Description=Exif metadata ingest module ExifParserFileIngestModule.moduleName.text=Exif Parser diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle.properties-MERGED index cfaadf1635..5063bd55fa 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/Bundle.properties-MERGED @@ -36,27 +36,27 @@ FileExtMismatchSettingsPanel.jLabel1.text=File Types: FileExtMismatchSettingsPanel.newExtButton.text=New Extension FileExtMismatchSettingsPanel.newMimePrompt.message=Add a new MIME file type: FileExtMismatchSettingsPanel.newMimePrompt.title=New MIME -FileExtMismatchSettingsPanel.newMimePrompt.emptyMime.message=MIME type text is empty\! +FileExtMismatchSettingsPanel.newMimePrompt.emptyMime.message=MIME type text is empty! FileExtMismatchSettingsPanel.newMimePrompt.emptyMime.title=Empty type -FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotSupported.message=MIME type not supported\! +FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotSupported.message=MIME type not supported! FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotSupported.title=Type not supported -FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeExists.message=MIME type already exists\! +FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeExists.message=MIME type already exists! FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeExists.title=Type already exists FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotDetectable.message=MIME type is not detectable by this module. FileExtMismatchSettingsPanel.newMimePrompt.mimeTypeNotDetectable.title=Type not detectable -FileExtMismatchSettingsPanel.removeTypeButton.noneSelected.message=No MIME type selected\! +FileExtMismatchSettingsPanel.removeTypeButton.noneSelected.message=No MIME type selected! FileExtMismatchSettingsPanel.removeTypeButton.noneSelected.title=No type selected FileExtMismatchSettingsPanel.newExtPrompt.message=Add an allowed extension: FileExtMismatchSettingsPanel.newExtPrompt.title=New allowed extension -FileExtMismatchSettingsPanel.newExtPrompt.empty.message=Extension text is empty\! +FileExtMismatchSettingsPanel.newExtPrompt.empty.message=Extension text is empty! FileExtMismatchSettingsPanel.newExtPrompt.empty.title=Extension text empty -FileExtMismatchSettingsPanel.newExtPrompt.noMimeType.message=No MIME type selected\! +FileExtMismatchSettingsPanel.newExtPrompt.noMimeType.message=No MIME type selected! FileExtMismatchSettingsPanel.newExtPrompt.noMimeType.title=No MIME type selected -FileExtMismatchSettingsPanel.newExtPrompt.extExists.message=Extension already exists\! +FileExtMismatchSettingsPanel.newExtPrompt.extExists.message=Extension already exists! FileExtMismatchSettingsPanel.newExtPrompt.extExists.title=Extension already exists -FileExtMismatchSettingsPanel.removeExtButton.noneSelected.message=No extension selected\! +FileExtMismatchSettingsPanel.removeExtButton.noneSelected.message=No extension selected! FileExtMismatchSettingsPanel.removeExtButton.noneSelected.title=No extension selected -FileExtMismatchSettingsPanel.removeExtButton.noMimeTypeSelected.message=No MIME type selected\! +FileExtMismatchSettingsPanel.removeExtButton.noMimeTypeSelected.message=No MIME type selected! FileExtMismatchSettingsPanel.removeExtButton.noMimeTypeSelected.title=No MIME type selected FileExtMismatchSettingsPanel.removeTypeButton.toolTipText= FileExtMismatchModuleSettingsPanel.checkAllRadioButton.text=Check all file types diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties-MERGED index 2d105bc3a9..ed79db018d 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties-MERGED @@ -40,10 +40,7 @@ ImportCentralRepoDbProgressDialog.errorParsingFile.message=Error parsing hash se ImportCentralRepoDbProgressDialog.linesProcessed.message=\ hashes processed ImportCentralRepoDbProgressDialog.title.text=Central Repository Import Progress OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=\ - Hash Set ingest module. \n\n\ - The ingest module analyzes files in the disk image and marks them as "known" (based on NSRL hashset lookup for "known" files) and "bad / interesting" (based on one or more hash sets supplied by the user).\n\n\ - The module also contains additional non-ingest tools that are integrated in the GUI, such as file lookup by hash and hash set configuration. +OpenIDE-Module-Long-Description=Hash Set ingest module. \n\nThe ingest module analyzes files in the disk image and marks them as "known" (based on NSRL hashset lookup for "known" files) and "bad / interesting" (based on one or more hash sets supplied by the user).\n\nThe module also contains additional non-ingest tools that are integrated in the GUI, such as file lookup by hash and hash set configuration. OpenIDE-Module-Name=HashDatabases OptionsCategory_Name_HashDatabase=Hash Sets OptionsCategory_Keywords_HashDatabase=Hash Sets @@ -172,10 +169,7 @@ HashDbSearchThread.name.searching=Searching HashDbSearchThread.noMoreFilesWithMD5Msg=No other files with the same MD5 hash were found. ModalNoButtons.indexingDbsTitle=Indexing hash sets ModalNoButtons.indexingDbTitle=Indexing hash set -ModalNoButtons.exitHashDbIndexingMsg=You are about to exit out of indexing your hash sets. \n\ -The generated index will be left unusable. If you choose to continue,\n\ - please delete the corresponding -md5.idx file in the hash folder.\n\ - Exit indexing? +ModalNoButtons.exitHashDbIndexingMsg=You are about to exit out of indexing your hash sets. \nThe generated index will be left unusable. If you choose to continue,\nplease delete the corresponding -md5.idx file in the hash folder.\nExit indexing? ModalNoButtons.dlgTitle.unfinishedIndexing=Unfinished Indexing ModalNoButtons.indexThis.currentlyIndexing1Db=Currently indexing 1 hash set ModalNoButtons.indexThese.currentlyIndexing1OfNDbs=Currently indexing 1 of {0} diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java index 85fa762300..ce3f971bee 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java @@ -163,6 +163,8 @@ final class ContactAnalyzer { data1 = resultSet.getString("data1"); //NON-NLS mimetype = resultSet.getString("mimetype"); //NON-NLS if (name.equals(oldName) == false) { + bba = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); + attributes = new ArrayList<>(); attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, moduleName, name)); } if (mimetype.equals("vnd.android.cursor.item/phone_v2")) { //NON-NLS @@ -170,6 +172,12 @@ final class ContactAnalyzer { } else { attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, moduleName, data1)); } + + // TODO: If this code comes back to life, add code to create the account + // and relationship between the phone numbers & emails. Also + // investigate if the mimetype "vnd.android.cursor.item/phone_v2" + // makes sense in an ios word + oldName = name; bba.addAttributes(attributes); diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED index 7ca4901b1b..ea423b415d 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED @@ -81,8 +81,8 @@ FilesSetRulePanel.nameTextField.text= FilesSetRulePanel.ruleNameLabel.text=Rule Name (Optional): FilesSetRulePanel.messages.emptyNameCondition=You must specify a name pattern for this rule. FilesSetRulePanel.messages.invalidNameRegex=The name regular expression is not valid:\n\n{0} -FilesSetRulePanel.messages.invalidCharInName=The name cannot contain \\, /, :, *, ?, \", <, or > unless it is a regular expression. -FilesSetRulePanel.messages.invalidCharInPath=The path cannot contain \\, :, *, ?, \", <, or > unless it is a regular expression. +FilesSetRulePanel.messages.invalidCharInName=The name cannot contain \\, /, :, *, ?, ", <, or > unless it is a regular expression. +FilesSetRulePanel.messages.invalidCharInPath=The path cannot contain \\, :, *, ?, ", <, or > unless it is a regular expression. FilesSetRulePanel.messages.invalidPathRegex=The path regular expression is not valid:\n\n{0} FilesSetDefsPanel.doFileSetsDialog.duplicateRuleSet.text=Rule set with name {0} already exists. FilesSetRulePanel.pathSeparatorInfoLabel.text=Use / as path separator diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED index 87dacfc16c..2dc971a40d 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED @@ -21,7 +21,7 @@ PhotoRecIngestModule.complete.totalParsetime=Total Parsing Time: PhotoRecIngestModule.complete.photoRecResults=PhotoRec Results PhotoRecIngestModule.NotEnoughDiskSpace.detail.msg=PhotoRec error processing {0} with {1} Not enough space on primary disk to save unallocated space. PhotoRecIngestModule.cancelledByUser=PhotoRec cancelled by user. -PhotoRecIngestModule.error.exitValue=PhotoRec carver returned error exit value \= {0} when scanning {1} +PhotoRecIngestModule.error.exitValue=PhotoRec carver returned error exit value = {0} when scanning {1} PhotoRecIngestModule.error.msg=Error processing {0} with PhotoRec carver. PhotoRecIngestModule.complete.numberOfErrors=Number of Errors while Carving: PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text=PhotoRec Settings diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalRegistryObj.java b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalRegistryObj.java index b5a4662ec6..8c41b87042 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalRegistryObj.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalRegistryObj.java @@ -1,15 +1,15 @@ /* * 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"); * 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. @@ -18,12 +18,15 @@ */ package org.sleuthkit.autopsy.modules.stix; +import com.williballenthin.rejistry.RegistryHiveFile; +import com.williballenthin.rejistry.RegistryKey; +import com.williballenthin.rejistry.RegistryParseException; +import com.williballenthin.rejistry.RegistryValue; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.Content; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.AbstractFile; - import java.util.List; import java.util.ArrayList; import java.io.IOException; @@ -31,10 +34,8 @@ import java.io.UnsupportedEncodingException; import java.io.File; import java.util.regex.Pattern; import java.util.regex.Matcher; - import org.mitre.cybox.objects.WindowsRegistryKey; import org.mitre.cybox.common_2.ConditionTypeEnum; -import com.williballenthin.rejistry.*; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** @@ -43,9 +44,9 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; class EvalRegistryObj extends EvaluatableObject { private final WindowsRegistryKey obj; - private final List regFiles = new ArrayList(); + private final List regFiles = new ArrayList<>(); - public EvalRegistryObj(WindowsRegistryKey a_obj, String a_id, String a_spacing, List a_regFiles) { + EvalRegistryObj(WindowsRegistryKey a_obj, String a_id, String a_spacing, List a_regFiles) { obj = a_obj; id = a_id; spacing = a_spacing; @@ -80,7 +81,7 @@ class EvalRegistryObj extends EvaluatableObject { setUnsupportedFieldWarnings(); // Make a list of hives to test - List hiveList = new ArrayList(); + List hiveList = new ArrayList<>(); if (obj.getHive() == null) { // If the hive field is missing, add everything hiveList.addAll(regFiles); @@ -88,9 +89,9 @@ class EvalRegistryObj extends EvaluatableObject { // If the hive name is HKEY_LOCAL_MACHINE, add the ones from the config directory. // Otherwise, add the others for (RegistryFileInfo regFile : regFiles) { - if (regFile.abstractFile.getParentPath() != null) { + if (regFile.getAbstractFile().getParentPath() != null) { Pattern pattern = Pattern.compile("system32", Pattern.CASE_INSENSITIVE); - Matcher matcher = pattern.matcher(regFile.abstractFile.getParentPath()); + Matcher matcher = pattern.matcher(regFile.getAbstractFile().getParentPath()); if (matcher.find()) { // Looking for system files and found one, so add it to the list if (obj.getHive().getValue().toString().equalsIgnoreCase("HKEY_LOCAL_MACHINE")) { //NON-NLS @@ -112,7 +113,7 @@ class EvalRegistryObj extends EvaluatableObject { Pattern pattern = Pattern.compile("Temp.STIX." + stixHiveName, Pattern.CASE_INSENSITIVE); for (RegistryFileInfo hive : regFiles) { - Matcher matcher = pattern.matcher(hive.tempFileName); + Matcher matcher = pattern.matcher(hive.getTempFileName()); if (matcher.find()) { hiveList.add(hive); } @@ -163,7 +164,7 @@ class EvalRegistryObj extends EvaluatableObject { */ private ObservableResult testRegistryFile(RegistryFileInfo a_regInfo) { try { - RegistryKey root = openRegistry(a_regInfo.tempFileName); + RegistryKey root = openRegistry(a_regInfo.getTempFileName()); RegistryKey result = findKey(root, obj.getKey().getValue().toString()); if (result == null) { @@ -192,8 +193,8 @@ class EvalRegistryObj extends EvaluatableObject { if ((obj.getValues() == null) || (obj.getValues().getValues().isEmpty())) { // No values to test - List artData = new ArrayList(); - artData.add(new StixArtifactData(a_regInfo.abstractFile.getId(), id, "Registry")); //NON-NLS + List artData = new ArrayList<>(); + artData.add(new StixArtifactData(a_regInfo.getAbstractFile().getId(), id, "Registry")); //NON-NLS return new ObservableResult(id, "RegistryObject: Found key " + obj.getKey().getValue(), //NON-NLS spacing, ObservableResult.ObservableState.TRUE, artData); } @@ -262,8 +263,8 @@ class EvalRegistryObj extends EvaluatableObject { if (nameSuccess && valueSuccess) { // Found a match for all values - List artData = new ArrayList(); - artData.add(new StixArtifactData(a_regInfo.abstractFile.getId(), id, "Registry")); //NON-NLS + List artData = new ArrayList<>(); + artData.add(new StixArtifactData(a_regInfo.getAbstractFile().getId(), id, "Registry")); //NON-NLS return new ObservableResult(id, "RegistryObject: Found key " + obj.getKey().getValue() //NON-NLS + " and value " + stixRegValue.getName().getValue().toString() //NON-NLS + " = " + stixRegValue.getData().getValue().toString(), @@ -343,13 +344,13 @@ class EvalRegistryObj extends EvaluatableObject { List regFilesAbstract = findRegistryFiles(); // List to hold all the extracted file names plus their abstract file - List regFilesLocal = new ArrayList(); + List regFilesLocal = new ArrayList<>(); // Make the temp directory String tmpDir; try { - tmpDir = Case.getCurrentCaseThrows().getTempDirectory() + File.separator + "STIX"; //NON-NLS - } catch (NoCurrentCaseException ex) { + tmpDir = Case.getCurrentCaseThrows().getTempDirectory() + File.separator + "STIX"; //NON-NLS + } catch (NoCurrentCaseException ex) { throw new TskCoreException(ex.getLocalizedMessage()); } File dir = new File(tmpDir); @@ -382,11 +383,11 @@ class EvalRegistryObj extends EvaluatableObject { * RecentActivity */ private static List findRegistryFiles() throws TskCoreException { - List registryFiles = new ArrayList(); + List registryFiles = new ArrayList<>(); Case openCase; try { openCase = Case.getCurrentCaseThrows(); - } catch (NoCurrentCaseException ex) { + } catch (NoCurrentCaseException ex) { throw new TskCoreException(ex.getLocalizedMessage()); } org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = openCase.getServices().getFileManager(); @@ -413,7 +414,7 @@ class EvalRegistryObj extends EvaluatableObject { } private void setUnsupportedFieldWarnings() { - List fieldNames = new ArrayList(); + List fieldNames = new ArrayList<>(); if (obj.getNumberValues() != null) { fieldNames.add("Number_Values"); //NON-NLS @@ -462,5 +463,23 @@ class EvalRegistryObj extends EvaluatableObject { tempFileName = a_tempFileName; } + /** + * Get the AbstractFile for this RegistryFileInfo + * + * @return the abstractFile + */ + AbstractFile getAbstractFile() { + return abstractFile; + } + + /** + * Get the Temporary file name for this RegistryFileInfo + * + * @return the tempFileName + */ + String getTempFileName() { + return tempFileName; + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/report/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/report/Bundle.properties-MERGED index f9813728d5..6444541518 100644 --- a/Core/src/org/sleuthkit/autopsy/report/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/report/Bundle.properties-MERGED @@ -99,7 +99,7 @@ FileReportDataTypes.path.text=Full Path FileReportText.getName.text=Files - Text FileReportText.getDesc.text=A delimited text file containing information about individual files in the case. ReportBodyFile.progress.querying=Querying files... -ReportBodyFile.ingestWarning.text=Warning, this report was run before ingest services completed\! +ReportBodyFile.ingestWarning.text=Warning, this report was run before ingest services completed! ReportBodyFile.progress.loading=Loading files... ReportBodyFile.progress.processing=Now processing {0}... ReportBodyFile.getName.text=TSK Body File @@ -241,13 +241,13 @@ ReportHTML.getName.text=HTML Report ReportHTML.getDesc.text=A report about results and tagged items in HTML format. ReportHTML.writeIndex.title=for case {0} ReportHTML.writeIndex.noFrames.msg=Your browser is not compatible with our frame setup. -ReportHTML.writeIndex.noFrames.seeNav=Please see the navigation page for artifact links, -ReportHTML.writeIndex.seeSum=and the summary page for a case summary. +ReportHTML.writeIndex.noFrames.seeNav=Please see the navigation page for artifact links, +ReportHTML.writeIndex.seeSum=and the summary page for a case summary. ReportHTML.writeNav.title=Report Navigation ReportHTML.writeNav.h1=Report Navigation ReportHTML.writeNav.summary=Case Summary ReportHTML.writeSum.title=Case Summary -ReportHTML.writeSum.warningMsg=Warning, this report was run before ingest services completed\! +ReportHTML.writeSum.warningMsg=Warning, this report was run before ingest services completed! # # autopsy/test/scripts/regression.py._html_report_diff() uses reportGenOn.text, caseName, caseNum, # examiner as a regex signature to skip report.html and summary.html diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/HtmlTextExtractor.java b/Core/src/org/sleuthkit/autopsy/textextractors/HtmlTextExtractor.java index defcdc23a5..d117e80537 100644 --- a/Core/src/org/sleuthkit/autopsy/textextractors/HtmlTextExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/textextractors/HtmlTextExtractor.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"); @@ -22,7 +22,9 @@ import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.logging.Level; import net.htmlparser.jericho.Attributes; import net.htmlparser.jericho.Config; @@ -71,7 +73,7 @@ final class HtmlTextExtractor implements TextExtractor { /** * Determines if this content type is supported by this extractor. * - * @param content Content instance to be analyzed + * @param content Content instance to be analyzed * @param detectedFormat Mimetype of content instance * * @return flag indicating support @@ -84,27 +86,21 @@ final class HtmlTextExtractor implements TextExtractor { } /** - * Returns a reader that will iterate over the text of an HTML document. - * - * @param content Html document source - * - * @return A reader instance containing the document source text - * - * @throws TextExtractorException + * Get the metadata as a key -> value map. HTML metadata will include + * scripts, links, images, comments, and misc attributes. + * + * @return Map containing metadata key -> value pairs. */ @Override - public Reader getReader() throws InitReaderException { - //TODO JIRA-4467, there is only harm in excluding HTML documents greater - //than 50MB due to our troubled approach of extraction. - ReadContentInputStream stream = new ReadContentInputStream(file); - - //Parse the stream with Jericho and put the results in a Reader + public Map getMetadata() { + Map metadataMap = new HashMap<>(); try { - StringBuilder scripts = new StringBuilder(); - StringBuilder links = new StringBuilder(); - StringBuilder images = new StringBuilder(); - StringBuilder comments = new StringBuilder(); - StringBuilder others = new StringBuilder(); + ReadContentInputStream stream = new ReadContentInputStream(file); + StringBuilder scripts = new StringBuilder("\n"); + StringBuilder links = new StringBuilder("\n"); + StringBuilder images = new StringBuilder("\n"); + StringBuilder comments = new StringBuilder("\n"); + StringBuilder others = new StringBuilder("\n"); int numScripts = 0; int numLinks = 0; int numImages = 0; @@ -113,17 +109,8 @@ final class HtmlTextExtractor implements TextExtractor { Source source = new Source(stream); source.fullSequentialParse(); - Renderer renderer = source.getRenderer(); - renderer.setNewLine("\n"); - renderer.setIncludeHyperlinkURLs(false); - renderer.setDecorateFontStyles(false); - renderer.setIncludeAlternateText(false); - String text = renderer.toString(); - // Get all the tags in the source List tags = source.getAllStartTags(); - - StringBuilder stringBuilder = new StringBuilder(); for (StartTag tag : tags) { if (tag.getName().equals("script")) { //NON-NLS // If the