diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AutoIngestCasePanelInterface.java b/Core/src/org/sleuthkit/autopsy/casemodule/AutoIngestCasePanelInterface.java new file mode 100755 index 0000000000..795b636b35 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AutoIngestCasePanelInterface.java @@ -0,0 +1,29 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.casemodule; + +import javax.swing.JDialog; + +/** + * Interface for startup window implementations + */ +public interface AutoIngestCasePanelInterface { + + public void addWindowStateListener(JDialog parent); +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index 8ba843a41c..1fc2209750 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -17,13 +17,7 @@ NewCaseVisualPanel1.jLabel2.text_1=Case data will be stored in the following dir NewCaseVisualPanel1.caseParentDirTextField.text= NewCaseVisualPanel1.caseDirTextField.text_1= CueBannerPanel.autopsyLogo.text= -CueBannerPanel.createNewLabel.text=Create New Case -CueBannerPanel.openLabel.text=Open Existing Case CueBannerPanel.closeButton.text=Close -CueBannerPanel.openRecentLabel.text=Open Recent Case -CueBannerPanel.newCaseButton.text= -CueBannerPanel.openCaseButton.text= -CueBannerPanel.openRecentButton.text= OpenRecentCasePanel.cancelButton.text=Cancel OpenRecentCasePanel.jLabel1.text=Recent Cases NewCaseVisualPanel2.caseNumberTextField.text= @@ -225,3 +219,11 @@ CasePropertiesPanel.lbDbType.text=Case Type: CasePropertiesPanel.examinerLabel.text=Examiner: CasePropertiesPanel.caseNumberLabel.text=Case Number: LocalDiskPanel.changeDatabasePathCheckbox.text=Update case to use VHD file upon completion +CueBannerPanel.openAutoIngestCaseButton.text= +CueBannerPanel.openExistingCaseButton.text= +CueBannerPanel.openRecentCaseButton.text= +CueBannerPanel.createNewCaseButton.text= +CueBannerPanel.createNewCaseLabel.text=Create New Case +CueBannerPanel.openRecentCaseLabel.text=Open Recent Case +CueBannerPanel.openExistingCaseLabel.text=Open Existing Case +CueBannerPanel.openAutoIngestCaseLabel.text=Open Auto Ingest Case diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties index 939c901e57..fd00f725e5 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties @@ -12,10 +12,7 @@ NewCaseVisualPanel1.caseNameLabel.text_1=\u30b1\u30fc\u30b9\u540d\uff1a NewCaseVisualPanel1.caseDirLabel.text=\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\uff1a NewCaseVisualPanel1.caseDirBrowseButton.text=\u95b2\u89a7 NewCaseVisualPanel1.jLabel2.text_1=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u306f\u6b21\u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306b\u4fdd\u5b58\u3055\u308c\u307e\u3059\uff1a -CueBannerPanel.createNewLabel.text=\u65b0\u898f\u30b1\u30fc\u30b9\u3092\u4f5c\u6210 -CueBannerPanel.openLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f CueBannerPanel.closeButton.text=\u9589\u3058\u308b -CueBannerPanel.openRecentLabel.text=\u6700\u8fd1\u958b\u3044\u305f\u30b1\u30fc\u30b9\u3092\u958b\u304f OpenRecentCasePanel.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb OpenRecentCasePanel.jLabel1.text=\u6700\u8fd1\u958b\u3044\u305f\u30d5\u30a1\u30a4\u30eb NewCaseVisualPanel2.examinerLabel.text=\u8abf\u67fb\u62c5\u5f53\u8005\uff1a @@ -196,3 +193,7 @@ CasePropertiesPanel.lbDbName.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u540d\uff CasePropertiesPanel.lbDbType.text=\u30b1\u30fc\u30b9\u30bf\u30a4\u30d7\uff1a CasePropertiesPanel.examinerLabel.text=\u8abf\u67fb\u62c5\u5f53\u8005\uff1a CasePropertiesPanel.caseNumberLabel.text=\u30b1\u30fc\u30b9\u756a\u53f7\uff1a +CueBannerPanel.createNewCaseLabel.text=\u65b0\u898f\u30b1\u30fc\u30b9\u3092\u4f5c\u6210 +CueBannerPanel.openRecentCaseLabel.text=\u6700\u8fd1\u958b\u3044\u305f\u30b1\u30fc\u30b9\u3092\u958b\u304f +CueBannerPanel.openExistingCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f +CueBannerPanel.openAutoIngestCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form index 223b83b7d4..75819e41d0 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form @@ -16,23 +16,27 @@ - - - - - - - - + @@ -159,10 +155,7 @@ - - - - + @@ -203,16 +196,6 @@ - - - - - - - - - - diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestSettingsPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestSettingsPanel.java index d64b9954ab..4877ce9d60 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestSettingsPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestSettingsPanel.java @@ -210,35 +210,16 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel { boolean needsRestart = false; UserPreferences.SelectedMode storedMode = UserPreferences.getMode(); - if (AutoIngestUserPreferences.getJoinAutoModeCluster() != cbJoinAutoIngestCluster.isSelected()) { - needsRestart = true; - } - AutoIngestUserPreferences.setJoinAutoModeCluster(cbJoinAutoIngestCluster.isSelected()); if (!cbJoinAutoIngestCluster.isSelected()) { + if(storedMode == UserPreferences.SelectedMode.AUTOINGEST) { + needsRestart = true; + } + UserPreferences.setMode(UserPreferences.SelectedMode.STANDALONE); - //before return popup the message - if (needsRestart) { - SwingUtilities.invokeLater(() -> { - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.MustRestart"), - NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.restartRequiredLabel.text"), - JOptionPane.WARNING_MESSAGE); - }); - } - return; } - - if (jRadioButtonAutomated.isSelected()) { - if (storedMode != UserPreferences.SelectedMode.AUTOINGEST) { - needsRestart = true; - } - String thePath = AutoIngestUserPreferences.getAutoModeImageFolder(); - if (thePath != null && 0 != inputPathTextField.getText().compareTo(thePath)) { - needsRestart = true; - } - thePath = AutoIngestUserPreferences.getAutoModeResultsFolder(); - if (thePath != null && 0 != outputPathTextField.getText().compareTo(thePath)) { + else if (jRadioButtonAutomated.isSelected()) { + if (storedMode == UserPreferences.SelectedMode.REVIEW) { needsRestart = true; } @@ -254,11 +235,7 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel { AutoIngestUserPreferences.setSharedConfigMaster(masterNodeCheckBox.isSelected()); } } else if (jRadioButtonReview.isSelected()) { - if (storedMode != UserPreferences.SelectedMode.REVIEW) { - needsRestart = true; - } - String thePath = AutoIngestUserPreferences.getAutoModeResultsFolder(); - if (thePath != null && 0 != outputPathTextField.getText().compareTo(thePath)) { + if (storedMode == UserPreferences.SelectedMode.AUTOINGEST) { needsRestart = true; } @@ -661,7 +638,6 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel { nodePanel = new javax.swing.JPanel(); jPanelNodeType = new javax.swing.JPanel(); jLabelSelectMode = new javax.swing.JLabel(); - restartRequiredNodeLabel = new javax.swing.JLabel(); jRadioButtonAutomated = new javax.swing.JRadioButton(); jRadioButtonReview = new javax.swing.JRadioButton(); jLabelSelectInputFolder = new javax.swing.JLabel(); @@ -703,9 +679,6 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(jLabelSelectMode, org.openide.util.NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.jLabelSelectMode.text")); // NOI18N - restartRequiredNodeLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/experimental/images/warning16.png"))); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(restartRequiredNodeLabel, org.openide.util.NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.restartRequiredNodeLabel.text")); // NOI18N - modeRadioButtons.add(jRadioButtonAutomated); jRadioButtonAutomated.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(jRadioButtonAutomated, org.openide.util.NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.jRadioButtonAutomated.text")); // NOI18N @@ -777,10 +750,7 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel { .addComponent(browseOutputFolderButton, javax.swing.GroupLayout.Alignment.TRAILING))) .addGroup(jPanelNodeTypeLayout.createSequentialGroup() .addGroup(jPanelNodeTypeLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanelNodeTypeLayout.createSequentialGroup() - .addComponent(jLabelSelectMode) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(restartRequiredNodeLabel)) + .addComponent(jLabelSelectMode) .addComponent(jRadioButtonReview) .addComponent(jRadioButtonAutomated)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) @@ -802,9 +772,7 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel { .addGroup(jPanelNodeTypeLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanelNodeTypeLayout.createSequentialGroup() .addContainerGap() - .addGroup(jPanelNodeTypeLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabelSelectMode) - .addComponent(restartRequiredNodeLabel)) + .addComponent(jLabelSelectMode) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(jRadioButtonAutomated) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -1407,7 +1375,6 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel { jRadioButtonAutomated.setEnabled(enabled); jRadioButtonReview.setEnabled(enabled); outputPathTextField.setEnabled(enabled); - restartRequiredNodeLabel.setEnabled(enabled); } // Variables declaration - do not modify//GEN-BEGIN:variables @@ -1442,7 +1409,6 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel { private javax.swing.JScrollPane nodeScrollPane; private javax.swing.JTextField outputPathTextField; private javax.swing.JProgressBar pbTaskInProgress; - private javax.swing.JLabel restartRequiredNodeLabel; private javax.swing.JCheckBox sharedConfigCheckbox; private javax.swing.JTextField sharedSettingsErrorTextField; private javax.swing.JTextField sharedSettingsTextField; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestUserPreferences.java b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestUserPreferences.java index f98eb1a909..8cd99b89c4 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestUserPreferences.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestUserPreferences.java @@ -59,7 +59,7 @@ public final class AutoIngestUserPreferences { } /** - * Get "Join Automated Ingest Cluster" setting from persistent storage. + * Get "Join auto ingest cluster" setting from persistent storage. * * @return SelectedMode Selected setting. */ @@ -71,7 +71,7 @@ public final class AutoIngestUserPreferences { } /** - * Set "Join Automated Ingest Cluster" setting to persistent storage. + * Set "Join auto ingest cluster" setting to persistent storage. * * @param join boolean value of whether to join auto ingest cluster or not */ diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/Bundle.properties b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/Bundle.properties index 21da10e84d..66ed50dd20 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/Bundle.properties +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/Bundle.properties @@ -25,7 +25,7 @@ AutoIngestSettingsPanel.AdvancedAutoIngestSettingsPanel.Title=Advanced Settings AutoIngestSettingsPanel.browseGlobalSettingsButton.text=Browse AutoIngestSettingsPanel.browseSharedSettingsButton.text=Browse AutoIngestSettingsPanel.CannotAccess=Cannot access -AutoIngestSettingsPanel.cbJoinAutoIngestCluster.text=Join Automated Ingest Cluster +AutoIngestSettingsPanel.cbJoinAutoIngestCluster.text=Join auto ingest cluster AutoIngestSettingsPanel.CheckPermissions=Check permissions. AutoIngestSettingsPanel.configButtonErrorTextField.text=configButtonErrorTextField AutoIngestSettingsPanel.downloadButton.text=Download Config @@ -89,7 +89,6 @@ OptionsDialog.jLabel1.text=jLabel1 StartupWindow.AutoIngestMode=Automated Ingest Node StartupWindow.CaseImportMode=Single User Case Import StartupWindow.CopyAndImportMode=Utilities -StartupWindow.ReviewMode=Cases StartupWindow.title.text=Welcome AdvancedAutoIngestSettingsPanel.lbInputScanIntervalMinutes.text=minutes AdvancedAutoIngestSettingsPanel.lbTimeoutHours.text=hour(s) @@ -133,10 +132,9 @@ AutoIngestSettingsPanel.inputPathTextField.toolTipText=Input folder for automate AutoIngestSettingsPanel.inputPathTextField.text= AutoIngestSettingsPanel.jLabelSelectInputFolder.text=Select shared images folder: AutoIngestSettingsPanel.jRadioButtonReview.toolTipText=Review cases created in automated processing mode -AutoIngestSettingsPanel.jRadioButtonReview.text=Examiner Node +AutoIngestSettingsPanel.jRadioButtonReview.text=Examiner node AutoIngestSettingsPanel.jRadioButtonAutomated.toolTipText=Automatically detect new data sources and create cases. -AutoIngestSettingsPanel.jRadioButtonAutomated.text=Auto Ingest Node -AutoIngestSettingsPanel.restartRequiredNodeLabel.text=Application restart required +AutoIngestSettingsPanel.jRadioButtonAutomated.text=Auto ingest node (application restart required) AutoIngestSettingsPanel.jLabelSelectMode.text=Select mode: AutoIngestSettingsPanel.jPanelIngestSettings.border.title=Automated Ingest Settings AutoIngestSettingsPanel.bnLogging.text=Node Status Logging diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/StartupWindow.java b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/StartupWindow.java index f36fa45f12..35d44dc407 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/StartupWindow.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/StartupWindow.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.experimental.configuration; -import java.awt.Cursor; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.ActionEvent; @@ -36,7 +35,6 @@ import org.sleuthkit.autopsy.casemodule.StartupWindowInterface; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.NetworkUtils; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestControlPanel; -import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestCasePanel; /** * The default implementation of the Autopsy startup window @@ -48,7 +46,6 @@ public final class StartupWindow extends JDialog implements StartupWindowInterfa private static Dimension DIMENSIONS = new Dimension(750, 400); private static CueBannerPanel welcomeWindow; private static final long serialVersionUID = 1L; - private AutoIngestCasePanel caseManagementPanel = null; private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName(); public StartupWindow() { @@ -78,12 +75,6 @@ public final class StartupWindow extends JDialog implements StartupWindowInterfa @Override public void open() { - - if (caseManagementPanel != null) { - caseManagementPanel.updateView(); - caseManagementPanel.setCursor(Cursor.getDefaultCursor()); - } - if (welcomeWindow != null) { welcomeWindow.refresh(); } @@ -104,38 +95,27 @@ public final class StartupWindow extends JDialog implements StartupWindowInterfa * user. */ private void addPanelForMode() { - UserPreferences.SelectedMode mode = UserPreferences.getMode(); - - switch (mode) { - case AUTOINGEST: - this.setTitle(NbBundle.getMessage(StartupWindow.class, "StartupWindow.AutoIngestMode") + " (" + LOCAL_HOST_NAME + ")"); - setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/frame.gif", false)); //NON-NLS - this.addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - AutoIngestControlPanel.getInstance().shutdown(); - } - }); - setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); - add(AutoIngestControlPanel.getInstance()); - break; - case REVIEW: - this.setTitle(NbBundle.getMessage(StartupWindow.class, "StartupWindow.ReviewMode") + " (" + LOCAL_HOST_NAME + ")"); - caseManagementPanel = new AutoIngestCasePanel(this); - setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/frame.gif", false)); //NON-NLS - add(caseManagementPanel); - break; - default: - welcomeWindow = new CueBannerPanel(); - // add the command to close the window to the button on the Volume Detail Panel - welcomeWindow.setCloseButtonActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - close(); - } - }); - add(welcomeWindow); - break; + if(UserPreferences.getMode() == UserPreferences.SelectedMode.AUTOINGEST) { + this.setTitle(NbBundle.getMessage(StartupWindow.class, "StartupWindow.AutoIngestMode") + " (" + LOCAL_HOST_NAME + ")"); + setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/frame.gif", false)); //NON-NLS + this.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + AutoIngestControlPanel.getInstance().shutdown(); + } + }); + setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + add(AutoIngestControlPanel.getInstance()); + } else { + welcomeWindow = new CueBannerPanel(); + // add the command to close the window to the button on the Volume Detail Panel + welcomeWindow.setCloseButtonActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + close(); + } + }); + add(welcomeWindow); } } } \ No newline at end of file diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java index cb7aac9018..e28cd75654 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java @@ -138,12 +138,14 @@ final class GlobalListSettingsPanel extends javax.swing.JPanel implements Option @Override public void addPropertyChangeListener(PropertyChangeListener l) { + super.addPropertyChangeListener(l); listsManagementPanel.addPropertyChangeListener(l); editListPanel.addPropertyChangeListener(l); } @Override public void removePropertyChangeListener(PropertyChangeListener l) { + super.removePropertyChangeListener(l); listsManagementPanel.removePropertyChangeListener(l); editListPanel.removePropertyChangeListener(l); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java index b8bb93b240..6836483b30 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java @@ -104,13 +104,13 @@ class HighlightedText implements IndexedText { /** * This constructor is used when keyword hits are accessed from the ad-hoc * search results. In that case we have the entire QueryResults object and - * need to arrange the paging. + need to arrange the paging. * * @param objectId The objectID of the content whose text will be * highlighted. * @param QueryResults The QueryResults for the ad-hoc search from whose - * results a selection was made leading to this - * HighlightedText. + results a selection was made leading to this + HighlightedText. */ HighlightedText(long objectId, QueryResults hits) { this.objectId = objectId; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSettingsPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSettingsPanel.java index 1756bfc1ec..93fa053dd4 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSettingsPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSettingsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,11 +28,12 @@ import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; */ final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { + private static final long serialVersionUID = 1L; private GlobalListSettingsPanel listsPanel; private KeywordSearchGlobalLanguageSettingsPanel languagesPanel; private KeywordSearchGlobalSearchSettingsPanel generalPanel; - public KeywordSearchGlobalSettingsPanel() { + KeywordSearchGlobalSettingsPanel() { initComponents(); customizeComponents(); } @@ -53,6 +54,7 @@ final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsP @Override public void addPropertyChangeListener(PropertyChangeListener l) { + super.addPropertyChangeListener(l); listsPanel.addPropertyChangeListener(l); languagesPanel.addPropertyChangeListener(l); generalPanel.addPropertyChangeListener(l); @@ -60,6 +62,7 @@ final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsP @Override public void removePropertyChangeListener(PropertyChangeListener l) { + super.removePropertyChangeListener(l); listsPanel.removePropertyChangeListener(l); languagesPanel.removePropertyChangeListener(l); generalPanel.removePropertyChangeListener(l); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java index da269e2d2c..4f71b18f5c 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java @@ -101,20 +101,20 @@ interface KeywordSearchQuery { String getEscapedQueryString(); /** - * Converts the keyword hits for a given search term into artifacts. + * Posts a keyword hit artifact to the blackboard for a given keyword hit. * - * @param content The Content object associated with the hit. + * @param content The text source object for the hit. * @param foundKeyword The keyword that was found by the search, this may be * different than the Keyword that was searched if, for * example, it was a RegexQuery. * @param hit The keyword hit. - * @param snippet The document snippet that contains the hit. + * @param snippet A snippet from the text that contains the hit. * @param listName The name of the keyword list that contained the * keyword for which the hit was found. * * - * @return The newly created artifact or Null if there was a problem + * @return The newly created artifact or null if there was a problem * creating it. */ - BlackboardArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName); + BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java index ac7cfae557..a48b40e756 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java @@ -331,7 +331,7 @@ class KeywordSearchResultFactory extends ChildFactory { final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) + " ..." : queryStr; try { progress = ProgressHandle.createHandle(NbBundle.getMessage(this.getClass(), "KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(true)); - hits.writeAllHitsToBlackBoard(progress, null, this, false); + hits.process(progress, null, this, false); } finally { finalizeWorker(); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java index 4d3f9cc5b9..e13531da26 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java @@ -52,8 +52,8 @@ class LuceneQuery implements KeywordSearchQuery { private static final Logger logger = Logger.getLogger(LuceneQuery.class.getName()); private String keywordStringEscaped; private boolean isEscaped; - private final Keyword originalKeyword ; - private final KeywordList keywordList ; + private final Keyword originalKeyword; + private final KeywordList keywordList; private final List filters = new ArrayList<>(); private String field = null; private static final int MAX_RESULTS_PER_CURSOR_MARK = 512; @@ -70,7 +70,7 @@ class LuceneQuery implements KeywordSearchQuery { LuceneQuery(KeywordList keywordList, Keyword keyword) { this.keywordList = keywordList; this.originalKeyword = keyword; - this.keywordStringEscaped = this.originalKeyword.getSearchTerm(); + this.keywordStringEscaped = this.originalKeyword.getSearchTerm(); } @Override @@ -191,8 +191,24 @@ class LuceneQuery implements KeywordSearchQuery { return StringUtils.isNotBlank(originalKeyword.getSearchTerm()); } + /** + * Posts a keyword hit artifact to the blackboard for a given keyword hit. + * + * @param content The text source object for the hit. + * @param foundKeyword The keyword that was found by the search, this may be + * different than the Keyword that was searched if, for + * example, it was a RegexQuery. + * @param hit The keyword hit. + * @param snippet A snippet from the text that contains the hit. + * @param listName The name of the keyword list that contained the + * keyword for which the hit was found. + * + * + * @return The newly created artifact or null if there was a problem + * creating it. + */ @Override - public BlackboardArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { + public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); Collection attributes = new ArrayList<>(); @@ -225,11 +241,9 @@ class LuceneQuery implements KeywordSearchQuery { } } - hit.getArtifactID().ifPresent(artifactID -> attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) ); - try { bba.addAttributes(attributes); //write out to bb @@ -398,10 +412,10 @@ class LuceneQuery implements KeywordSearchQuery { return EscapeUtil.unEscapeHtml(contentHighlights.get(0)).trim(); } } catch (NoOpenCoreException ex) { - logger.log(Level.SEVERE, "Error executing Lucene Solr Query: " + query +". Solr doc id " + solrObjectId + ", chunkID " + chunkID , ex); //NON-NLS + logger.log(Level.SEVERE, "Error executing Lucene Solr Query: " + query + ". Solr doc id " + solrObjectId + ", chunkID " + chunkID, ex); //NON-NLS throw ex; } catch (KeywordSearchModuleException ex) { - logger.log(Level.SEVERE, "Error executing Lucene Solr Query: " + query +". Solr doc id " + solrObjectId + ", chunkID " + chunkID , ex); //NON-NLS + logger.log(Level.SEVERE, "Error executing Lucene Solr Query: " + query + ". Solr doc id " + solrObjectId + ", chunkID " + chunkID, ex); //NON-NLS return ""; } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java index 48fdb38f10..7037f898be 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java @@ -45,195 +45,274 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * Stores the results from running a Solr query (which could contain multiple - * keywords). - * + * Stores and processes the results of a keyword search query. Processing + * includes posting keyword hit artifacts to the blackboard, sending messages + * about the search hits to the ingest inbox, and publishing an event to notify + * subscribers of the blackboard posts. */ class QueryResults { private static final Logger logger = Logger.getLogger(QueryResults.class.getName()); private static final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); - /** - * The query that generated the results. - */ - private final KeywordSearchQuery keywordSearchQuery; - - /** - * A map of keywords to keyword hits. - */ + private final KeywordSearchQuery query; private final Map> results = new HashMap<>(); + /** + * Constructs a object that stores and processes the results of a keyword + * search query. Processing includes posting keyword hit artifacts to the + * blackboard, sending messages about the search hits to the ingest inbox, + * and publishing an event to notify subscribers of the blackboard posts. + * + * The KeywordSearchQuery is used to do the blackboard posts. + * + * @param query The query. + */ QueryResults(KeywordSearchQuery query) { - this.keywordSearchQuery = query; + this.query = query; } + /** + * Gets the keyword search query that generated the results stored in this + * object. + * + * @return The query. + */ + KeywordSearchQuery getQuery() { + return query; + } + + /** + * Adds the keyword hits for a keyword to the hits that are stored in this + * object. All calls to this method MUST be completed before calling the + * process method. + * + * @param keyword The keyword, + * @param hits The hits. + */ void addResult(Keyword keyword, List hits) { results.put(keyword, hits); } - KeywordSearchQuery getQuery() { - return keywordSearchQuery; - } - + /** + * Gets the keyword hits stored in this object for a given keyword. + * + * @param keyword The keyword. + * + * @return The keyword hits. + */ List getResults(Keyword keyword) { return results.get(keyword); } + /** + * Gets the set of unique keywords for which keyword hits have been stored + * in this object. + * + * @return + */ Set getKeywords() { return results.keySet(); } /** - * Writes the keyword hits encapsulated in this query result to the - * blackboard. Makes one artifact per keyword per searched object (file or - * artifact), i.e., if a keyword is found several times in the object, only - * one artifact is created. + * Processes the keyword hits stored in this object by osting keyword hit + * artifacts to the blackboard, sending messages about the search hits to + * the ingest inbox, and publishing an event to notify subscribers of the + * blackboard posts. * - * @param progress Can be null. - * @param subProgress Can be null. - * @param worker The Swing worker that is writing the hits, needed to - * support cancellation. - * @param notifyInbox Whether or not write a message to the ingest messages - * inbox. + * Makes one artifact per keyword per searched text source object (file or + * artifact), i.e., if a keyword is found several times in the text + * extracted from the source object, only one artifact is created. + * + * This method ASSUMES that the processing is being done using a SwingWorker + * that should be checked for task cancellation. + * + * All calls to the addResult method MUST be completed before calling this + * method. + * + * @param progress A progress indicator that reports the number of + * keywords processed. Can be null. + * @param subProgress A progress contributor that reports the keyword + * currently being processed. Can be null. + * @param worker The SwingWorker that is being used to do the + * processing, will be checked for task cancellation + * before processing each keyword. + * @param notifyInbox Whether or not to write a message to the ingest + * messages inbox if there is a keyword hit in the text + * exrtacted from the text source object. * - * @return The artifacts that were created. */ - Collection writeAllHitsToBlackBoard(ProgressHandle progress, ProgressContributor subProgress, SwingWorker worker, boolean notifyInbox) { - final Collection newArtifacts = new ArrayList<>(); - if (progress != null) { + void process(ProgressHandle progress, ProgressContributor subProgress, SwingWorker worker, boolean notifyInbox) { + /* + * Initialize the progress indicator to the number of keywords that will + * be processed. + */ + if (null != progress) { progress.start(getKeywords().size()); } - int unitProgress = 0; + /* + * Process the keyword hits for each keyword. + */ + int keywordsProcessed = 0; + final Collection hitArtifacts = new ArrayList<>(); for (final Keyword keyword : getKeywords()) { + /* + * Cancellation check. + */ if (worker.isCancelled()) { - logger.log(Level.INFO, "Cancel detected, bailing before new keyword processed: {0}", keyword.getSearchTerm()); //NON-NLS + logger.log(Level.INFO, "Processing cancelled, exiting before processing search term {0}", keyword.getSearchTerm()); //NON-NLS break; } - // Update progress object(s), if any + /* + * Update the progress indicator and the show the current keyword + * via the progress contributor. + */ if (progress != null) { - progress.progress(keyword.toString(), unitProgress); + progress.progress(keyword.toString(), keywordsProcessed); } if (subProgress != null) { String hitDisplayStr = keyword.getSearchTerm(); if (hitDisplayStr.length() > 50) { hitDisplayStr = hitDisplayStr.substring(0, 49) + "..."; } - subProgress.progress(keywordSearchQuery.getKeywordList().getName() + ": " + hitDisplayStr, unitProgress); + subProgress.progress(query.getKeywordList().getName() + ": " + hitDisplayStr, keywordsProcessed); } - for (KeywordHit hit : getOneHitPerObject(keyword)) { - String termString = keyword.getSearchTerm(); + /* + * Reduce the hits for this keyword to one hit per text source + * object so that only one hit artifact is generated per text source + * object, no matter how many times the keyword was actually found. + */ + for (KeywordHit hit : getOneHitPerTextSourceObject(keyword)) { + /* + * Get a snippet (preview) for the hit. Regex queries always + * have snippets made from the content_str pulled back from Solr + * for executing the search. Other types of queries may or may + * not have snippets yet. + */ String snippet = hit.getSnippet(); if (StringUtils.isBlank(snippet)) { - final String snippetQuery = KeywordSearchUtil.escapeLuceneQuery(termString); + final String snippetQuery = KeywordSearchUtil.escapeLuceneQuery(keyword.getSearchTerm()); try { - /* - * this doesn't work for regex queries... But that is - * okay because regex queries always have snippets made - * from the content_str field we pull back from Solr - */ - snippet = LuceneQuery.querySnippet(snippetQuery, hit.getSolrObjectId(), hit.getChunkId(), !keywordSearchQuery.isLiteral(), true); + snippet = LuceneQuery.querySnippet(snippetQuery, hit.getSolrObjectId(), hit.getChunkId(), !query.isLiteral(), true); } catch (NoOpenCoreException e) { - logger.log(Level.WARNING, "Error querying snippet: " + snippetQuery, e); //NON-NLS - //no reason to continue - break; + logger.log(Level.SEVERE, "Solr core closed while executing snippet query " + snippetQuery, e); //NON-NLS + break; // Stop processing. } catch (Exception e) { - logger.log(Level.WARNING, "Error querying snippet: " + snippetQuery, e); //NON-NLS - continue; + logger.log(Level.SEVERE, "Error executing snippet query " + snippetQuery, e); //NON-NLS + continue; // Try processing the next hit. } } + + /* + * Get the content (file or artifact) that is the text source + * for the hit. + */ Content content = null; try { SleuthkitCase tskCase = Case.getCurrentCase().getSleuthkitCase(); content = tskCase.getContentById(hit.getContentID()); } catch (TskCoreException | IllegalStateException tskCoreException) { - logger.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", tskCoreException); //NON-NLS - return null; + logger.log(Level.SEVERE, "Failed to get text source object for ", tskCoreException); //NON-NLS } - BlackboardArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard(content, keyword, hit, snippet, keywordSearchQuery.getKeywordList().getName()); - if (writeResult != null) { - newArtifacts.add(writeResult); + + /* + * Post an artifact for the hit to the blackboard. + */ + BlackboardArtifact artifact = query.postKeywordHitToBlackboard(content, keyword, hit, snippet, query.getKeywordList().getName()); + if (null == artifact) { + logger.log(Level.SEVERE, "Error posting keyword hit artifact for keyword {0} in {1} to the blackboard", new Object[]{keyword.toString(), content}); //NON-NLS + } + + /* + * Send an ingest inbox message for the hit. + */ + if (null != artifact) { + hitArtifacts.add(artifact); if (notifyInbox) { try { - writeSingleFileInboxMessage(writeResult, content); + writeSingleFileInboxMessage(artifact, content); } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error posting message to Ingest Inbox", ex); //NON-NLS + logger.log(Level.SEVERE, "Error sending message to ingest messages inbox", ex); //NON-NLS } } - } else { - logger.log(Level.WARNING, "BB artifact for keyword hit not written, file: {0}, hit: {1}", new Object[]{content, keyword.toString()}); //NON-NLS } } - ++unitProgress; + + ++keywordsProcessed; } - // Update artifact browser - if (!newArtifacts.isEmpty()) { - newArtifacts.stream() - //group artifacts by type + /* + * Publish an event to notify subscribers of the blackboard posts. The + * artifacts are grouped by type, since they may contain both + * TSK_KEYWORD_HIT artifacts and TSK_ACCOUNT artifacts (for credit card + * account number hits). + */ + if (!hitArtifacts.isEmpty()) { + hitArtifacts.stream() + // Group artifacts by type .collect(Collectors.groupingBy(BlackboardArtifact::getArtifactTypeID)) - //for each type send an event + // For each type send an event .forEach((typeID, artifacts) -> IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.fromID(typeID), artifacts))); } - - return newArtifacts; } /** - * Gets the first hit of the keyword. + * Reduce the hits for a given keyword to one hit per text source object so + * that only one hit artifact is generated per text source object, no matter + * how many times the keyword was actually found. * - * @param keyword + * @param keyword The keyword. * - * @return Collection containing KeywordHits with lowest - * SolrObjectID-ChunkID pairs. + * @return Collection The reduced set of keyword hits. */ - private Collection getOneHitPerObject(Keyword keyword) { - HashMap hits = new HashMap<>(); - - // create a list of KeywordHits. KeywordHits with lowest chunkID is added the the list. - for (KeywordHit hit : getResults(keyword)) { + private Collection getOneHitPerTextSourceObject(Keyword keyword) { + /* + * For each Solr document (chunk) for a text source object, return only + * a single keyword hit from the first chunk of text (the one with the + * lowest chunk id). + */ + HashMap< Long, KeywordHit> hits = new HashMap<>(); + getResults(keyword).forEach((hit) -> { if (!hits.containsKey(hit.getSolrObjectId())) { hits.put(hit.getSolrObjectId(), hit); } else if (hit.getChunkId() < hits.get(hit.getSolrObjectId()).getChunkId()) { hits.put(hit.getSolrObjectId(), hit); } - } + }); return hits.values(); } /** - * Generate and post an ingest inbox message for the given keyword in the - * given content. + * Send an ingest inbox message indicating that there was a keyword hit in + * the given text source object. * - * @param artifact The keyword hit artifact. - * @param hitContent The content that the hit is in. + * @param artifact The keyword hit artifact for the hit. + * @param hitContent The text source object. * - * @throws TskCoreException If there is a problem generating or posting the + * @throws TskCoreException If there is a problem generating or send the * inbox message. */ private void writeSingleFileInboxMessage(BlackboardArtifact artifact, Content hitContent) throws TskCoreException { - StringBuilder subjectSb = new StringBuilder(); - StringBuilder detailsSb = new StringBuilder(); - - if (!keywordSearchQuery.isLiteral()) { + StringBuilder subjectSb = new StringBuilder(1024); + if (!query.isLiteral()) { subjectSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.regExpHitLbl")); } else { subjectSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitLbl")); } + StringBuilder detailsSb = new StringBuilder(1024); String uniqueKey = null; BlackboardAttribute attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD)); if (attr != null) { final String keyword = attr.getValueString(); subjectSb.append(keyword); uniqueKey = keyword.toLowerCase(); - //details detailsSb.append(""); //NON-NLS - //hit detailsSb.append(""); //NON-NLS detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitThLbl")); detailsSb.append(""); //NON-NLS @@ -270,7 +349,7 @@ class QueryResults { } //regex - if (!keywordSearchQuery.isLiteral()) { + if (!query.isLiteral()) { attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP)); if (attr != null) { detailsSb.append(""); //NON-NLS @@ -279,7 +358,6 @@ class QueryResults { detailsSb.append(""); //NON-NLS } } - detailsSb.append("
").append(EscapeUtil.escapeHtml(keyword)).append("
"); //NON-NLS IngestServices.getInstance().postMessage(IngestMessage.createDataMessage(MODULE_NAME, subjectSb.toString(), detailsSb.toString(), uniqueKey, artifact)); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index a456e6c029..ac5ea43acf 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -403,8 +403,24 @@ final class RegexQuery implements KeywordSearchQuery { return escapedQuery; } + /** + * Posts a keyword hit artifact to the blackboard for a given keyword hit. + * + * @param content The text source object for the hit. + * @param foundKeyword The keyword that was found by the search, this may be + * different than the Keyword that was searched if, for + * example, it was a RegexQuery. + * @param hit The keyword hit. + * @param snippet A snippet from the text that contains the hit. + * @param listName The name of the keyword list that contained the + * keyword for which the hit was found. + * + * + * @return The newly created artifact or null if there was a problem + * creating it. + */ @Override - public BlackboardArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { + public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); if (content == null) { @@ -413,96 +429,30 @@ final class RegexQuery implements KeywordSearchQuery { } /* - * Create either a "plain vanilla" keyword hit artifact with keyword and - * regex attributes, or a credit card account artifact with attributes - * parsed from from the snippet for the hit and looked up based on the - * parsed bank identifcation number. + * Credit Card number hits are handled differently + */ + if (originalKeyword.getArtifactAttributeType() == ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { + createCCNAccount(content, foundKeyword, hit, snippet, listName); + return null; + } + + /* + * Create a "plain vanilla" keyword hit artifact with keyword and + * regex attributes */ BlackboardArtifact newArtifact; Collection attributes = new ArrayList<>(); - if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm())); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, getQueryString())); - try { - newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS - return null; - } - } else { - /* - * Parse the credit card account attributes from the snippet for the - * hit. - */ - Map parsedTrackAttributeMap = new HashMap<>(); - Matcher matcher = TermsComponentQuery.CREDIT_CARD_TRACK1_PATTERN.matcher(hit.getSnippet()); - if (matcher.find()) { - parseTrack1Data(parsedTrackAttributeMap, matcher); - } - matcher = CREDIT_CARD_TRACK2_PATTERN.matcher(hit.getSnippet()); - if (matcher.find()) { - parseTrack2Data(parsedTrackAttributeMap, matcher); - } - final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER)); - if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) { - if (hit.isArtifactHit()) { - LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS - } else { - LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContentID())); //NON-NLS - } - return null; - } - attributes.addAll(parsedTrackAttributeMap.values()); - - /* - * Look up the bank name, scheme, etc. attributes for the bank - * indentification number (BIN). - */ - final int bin = Integer.parseInt(ccnAttribute.getValueString().substring(0, 8)); - CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin); - if (binInfo != null) { - binInfo.getScheme().ifPresent(scheme - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_SCHEME, MODULE_NAME, scheme))); - binInfo.getCardType().ifPresent(cardType - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_TYPE, MODULE_NAME, cardType))); - binInfo.getBrand().ifPresent(brand - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BRAND_NAME, MODULE_NAME, brand))); - binInfo.getBankName().ifPresent(bankName - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BANK_NAME, MODULE_NAME, bankName))); - binInfo.getBankPhoneNumber().ifPresent(phoneNumber - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, MODULE_NAME, phoneNumber))); - binInfo.getBankURL().ifPresent(url - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, MODULE_NAME, url))); - binInfo.getCountry().ifPresent(country - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNTRY, MODULE_NAME, country))); - binInfo.getBankCity().ifPresent(city - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CITY, MODULE_NAME, city))); - } - - /* - * If the hit is from unused or unallocated space, record the Solr - * document id to support showing just the chunk that contained the - * hit. - */ - if (content instanceof AbstractFile) { - AbstractFile file = (AbstractFile) content; - if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS - || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) { - attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId())); - } - } - - /* - * Create an account instance. - */ - try { - AccountInstance ccAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString() , MODULE_NAME, content); - newArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(ccAccountInstance.getArtifactId()); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error adding artifact for account to blackboard", ex); //NON-NLS - return null; - } + + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm())); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, getQueryString())); + + try { + newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS + return null; } + if (StringUtils.isNotBlank(listName)) { attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName)); @@ -526,6 +476,107 @@ final class RegexQuery implements KeywordSearchQuery { } } + private void createCCNAccount(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { + + final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); + + if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { + LOGGER.log(Level.SEVERE, "Keyword hit is not a credit card number"); //NON-NLS + return; + } + /* + * Create a credit card account with attributes + * parsed from the snippet for the hit and looked up based on the + * parsed bank identifcation number. + */ + Collection attributes = new ArrayList<>(); + + Map parsedTrackAttributeMap = new HashMap<>(); + Matcher matcher = TermsComponentQuery.CREDIT_CARD_TRACK1_PATTERN.matcher(hit.getSnippet()); + if (matcher.find()) { + parseTrack1Data(parsedTrackAttributeMap, matcher); + } + matcher = CREDIT_CARD_TRACK2_PATTERN.matcher(hit.getSnippet()); + if (matcher.find()) { + parseTrack2Data(parsedTrackAttributeMap, matcher); + } + final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER)); + if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) { + if (hit.isArtifactHit()) { + LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS + } else { + LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContentID())); //NON-NLS + } + return; + } + attributes.addAll(parsedTrackAttributeMap.values()); + + /* + * Look up the bank name, scheme, etc. attributes for the bank + * indentification number (BIN). + */ + final int bin = Integer.parseInt(ccnAttribute.getValueString().substring(0, 8)); + CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin); + if (binInfo != null) { + binInfo.getScheme().ifPresent(scheme + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_SCHEME, MODULE_NAME, scheme))); + binInfo.getCardType().ifPresent(cardType + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_TYPE, MODULE_NAME, cardType))); + binInfo.getBrand().ifPresent(brand + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BRAND_NAME, MODULE_NAME, brand))); + binInfo.getBankName().ifPresent(bankName + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BANK_NAME, MODULE_NAME, bankName))); + binInfo.getBankPhoneNumber().ifPresent(phoneNumber + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, MODULE_NAME, phoneNumber))); + binInfo.getBankURL().ifPresent(url + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, MODULE_NAME, url))); + binInfo.getCountry().ifPresent(country + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNTRY, MODULE_NAME, country))); + binInfo.getBankCity().ifPresent(city + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CITY, MODULE_NAME, city))); + } + + /* + * If the hit is from unused or unallocated space, record the Solr + * document id to support showing just the chunk that contained the + * hit. + */ + if (content instanceof AbstractFile) { + AbstractFile file = (AbstractFile) content; + if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS + || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) { + attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId())); + } + } + + if (StringUtils.isNotBlank(listName)) { + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName)); + } + if (snippet != null) { + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet)); + } + + hit.getArtifactID().ifPresent(artifactID + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) + ); + + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.REGEX.ordinal())); + + + /* + * Create an account instance. + */ + try { + AccountInstance ccAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString() , MODULE_NAME, content); + + ccAccountInstance.addAttributes(attributes); + + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error creating CCN account instance", ex); //NON-NLS + + } + + } /** * Parses the track 2 data from the snippet for a credit card account number * hit and turns them into artifact attributes. diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java index c9b95a770b..b998294fe4 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java @@ -492,7 +492,7 @@ public final class SearchRunner { subProgresses[keywordsSearched].progress(keywordList.getName() + ": " + queryDisplayStr, unitProgress); // Create blackboard artifacts - newResults.writeAllHitsToBlackBoard(null, subProgresses[keywordsSearched], this, keywordList.getIngestMessages()); + newResults.process(null, subProgresses[keywordsSearched], this, keywordList.getIngestMessages()); } //if has results diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java index c8ae73dc78..2be10ebe67 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java @@ -81,8 +81,8 @@ final class TermsComponentQuery implements KeywordSearchQuery { * digit is 2 through 6 * */ - static final Pattern CREDIT_CARD_NUM_PATTERN = - Pattern.compile("(?[2-6]([ -]?[0-9]){11,18})"); + static final Pattern CREDIT_CARD_NUM_PATTERN + = Pattern.compile("(?[2-6]([ -]?[0-9]){11,18})"); static final Pattern CREDIT_CARD_TRACK1_PATTERN = Pattern.compile( /* * Track 1 is alphanumeric. @@ -126,7 +126,6 @@ final class TermsComponentQuery implements KeywordSearchQuery { + "?)?)?)?)?)?"); //close nested optional groups //NON-NLS static final BlackboardAttribute.Type KEYWORD_SEARCH_DOCUMENT_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID); - /** * Constructs an object that implements a regex query that will be performed * as a two step operation. In the first step, the Solr terms component is @@ -327,101 +326,51 @@ final class TermsComponentQuery implements KeywordSearchQuery { return results; } + /** + * Posts a keyword hit artifact to the blackboard for a given keyword hit. + * + * @param content The text source object for the hit. + * @param foundKeyword The keyword that was found by the search, this may be + * different than the Keyword that was searched if, for + * example, it was a RegexQuery. + * @param hit The keyword hit. + * @param snippet A snippet from the text that contains the hit. + * @param listName The name of the keyword list that contained the + * keyword for which the hit was found. + * + * + * @return The newly created artifact or null if there was a problem + * creating it. + */ @Override - public BlackboardArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { + public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { + /* - * Create either a "plain vanilla" keyword hit artifact with keyword and - * regex attributes, or a credit card account artifact with attributes - * parsed from from the snippet for the hit and looked up based on the - * parsed bank identifcation number. + * CCN hits are handled specially + */ + if (originalKeyword.getArtifactAttributeType() == ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { + createCCNAccount(content, hit, snippet, listName); + return null; + } + + /* + * Create a "plain vanilla" keyword hit artifact with keyword and + * regex attributes, */ BlackboardArtifact newArtifact; Collection attributes = new ArrayList<>(); - if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm())); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, originalKeyword.getSearchTerm())); + + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm())); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, originalKeyword.getSearchTerm())); - try { - newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); + try { + newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS - return null; - } - } else { - /* - * Parse the credit card account attributes from the snippet for the - * hit. - */ - Map parsedTrackAttributeMap = new HashMap<>(); - Matcher matcher = CREDIT_CARD_TRACK1_PATTERN.matcher(hit.getSnippet()); - if (matcher.find()) { - parseTrack1Data(parsedTrackAttributeMap, matcher); - } - matcher = CREDIT_CARD_TRACK2_PATTERN.matcher(hit.getSnippet()); - if (matcher.find()) { - parseTrack2Data(parsedTrackAttributeMap, matcher); - } - final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER)); - if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) { - if (hit.isArtifactHit()) { - LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", searchTerm, hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS - } else { - LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", searchTerm, hit.getSnippet(), hit.getContentID())); //NON-NLS - } - return null; - } - attributes.addAll(parsedTrackAttributeMap.values()); - - /* - * Look up the bank name, scheme, etc. attributes for the bank - * indentification number (BIN). - */ - final int bin = Integer.parseInt(ccnAttribute.getValueString().substring(0, 8)); - CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin); - if (binInfo != null) { - binInfo.getScheme().ifPresent(scheme - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_SCHEME, MODULE_NAME, scheme))); - binInfo.getCardType().ifPresent(cardType - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_TYPE, MODULE_NAME, cardType))); - binInfo.getBrand().ifPresent(brand - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BRAND_NAME, MODULE_NAME, brand))); - binInfo.getBankName().ifPresent(bankName - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BANK_NAME, MODULE_NAME, bankName))); - binInfo.getBankPhoneNumber().ifPresent(phoneNumber - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, MODULE_NAME, phoneNumber))); - binInfo.getBankURL().ifPresent(url - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, MODULE_NAME, url))); - binInfo.getCountry().ifPresent(country - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNTRY, MODULE_NAME, country))); - binInfo.getBankCity().ifPresent(city - -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CITY, MODULE_NAME, city))); - } - - /* - * If the hit is from unused or unallocated space, record the Solr - * document id to support showing just the chunk that contained the - * hit. - */ - if (content instanceof AbstractFile) { - AbstractFile file = (AbstractFile) content; - if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS - || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) { - attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId())); - } - } - - /* - * Create an account artifact. - */ - try { - AccountInstance ccAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString() , MODULE_NAME, content); - newArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(ccAccountInstance.getArtifactId()); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error adding artifact for account to blackboard", ex); //NON-NLS - return null; - } + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS + return null; } + if (StringUtils.isNotBlank(listName)) { attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName)); @@ -446,6 +395,104 @@ final class TermsComponentQuery implements KeywordSearchQuery { } } + private void createCCNAccount(Content content, KeywordHit hit, String snippet, String listName) { + + if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { + LOGGER.log(Level.SEVERE, "Keyword hit is not a credit card number"); //NON-NLS + return; + } + + /* + * Create a credit card account with attributes + * parsed from from the snippet for the hit and looked up based on the + * parsed bank identifcation number. + */ + Collection attributes = new ArrayList<>(); + + Map parsedTrackAttributeMap = new HashMap<>(); + Matcher matcher = CREDIT_CARD_TRACK1_PATTERN.matcher(hit.getSnippet()); + if (matcher.find()) { + parseTrack1Data(parsedTrackAttributeMap, matcher); + } + matcher = CREDIT_CARD_TRACK2_PATTERN.matcher(hit.getSnippet()); + if (matcher.find()) { + parseTrack2Data(parsedTrackAttributeMap, matcher); + } + final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER)); + if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) { + if (hit.isArtifactHit()) { + LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", searchTerm, hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS + } else { + LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", searchTerm, hit.getSnippet(), hit.getContentID())); //NON-NLS + } + return; + } + attributes.addAll(parsedTrackAttributeMap.values()); + + /* + * Look up the bank name, scheme, etc. attributes for the bank + * indentification number (BIN). + */ + final int bin = Integer.parseInt(ccnAttribute.getValueString().substring(0, 8)); + CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin); + if (binInfo != null) { + binInfo.getScheme().ifPresent(scheme + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_SCHEME, MODULE_NAME, scheme))); + binInfo.getCardType().ifPresent(cardType + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_TYPE, MODULE_NAME, cardType))); + binInfo.getBrand().ifPresent(brand + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BRAND_NAME, MODULE_NAME, brand))); + binInfo.getBankName().ifPresent(bankName + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BANK_NAME, MODULE_NAME, bankName))); + binInfo.getBankPhoneNumber().ifPresent(phoneNumber + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, MODULE_NAME, phoneNumber))); + binInfo.getBankURL().ifPresent(url + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, MODULE_NAME, url))); + binInfo.getCountry().ifPresent(country + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNTRY, MODULE_NAME, country))); + binInfo.getBankCity().ifPresent(city + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CITY, MODULE_NAME, city))); + } + + /* + * If the hit is from unused or unallocated space, record the Solr + * document id to support showing just the chunk that contained the + * hit. + */ + if (content instanceof AbstractFile) { + AbstractFile file = (AbstractFile) content; + if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS + || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) { + attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId())); + } + } + + if (StringUtils.isNotBlank(listName)) { + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName)); + } + if (snippet != null) { + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet)); + } + + hit.getArtifactID().ifPresent( + artifactID -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) + ); + + // TermsComponentQuery is now being used exclusively for substring searches. + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal())); + + /* + * Create an account. + */ + try { + AccountInstance ccAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString() , MODULE_NAME, content); + ccAccountInstance.addAttributes(attributes); + //newArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(ccAccountInstance.getArtifactId()); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error creating CCN account instance", ex); //NON-NLS + } + + } /** * Parses the track 2 data from the snippet for a credit card account number * hit and turns them into artifact attributes. diff --git a/Testing/src/org/sleuthkit/autopsy/testing/AutopsyTestCases.java b/Testing/src/org/sleuthkit/autopsy/testing/AutopsyTestCases.java index 672ca5b22b..c716081672 100755 --- a/Testing/src/org/sleuthkit/autopsy/testing/AutopsyTestCases.java +++ b/Testing/src/org/sleuthkit/autopsy/testing/AutopsyTestCases.java @@ -36,6 +36,7 @@ import java.util.logging.Logger; import javax.imageio.ImageIO; import javax.swing.JDialog; import javax.swing.text.JTextComponent; +import javax.swing.tree.TreePath; import org.netbeans.jellytools.MainWindowOperator; import org.netbeans.jellytools.NbDialogOperator; import org.netbeans.jellytools.WizardOperator; @@ -53,6 +54,8 @@ import org.netbeans.jemmy.operators.JTabbedPaneOperator; import org.netbeans.jemmy.operators.JTableOperator; import org.netbeans.jemmy.operators.JTextFieldOperator; import org.netbeans.jemmy.operators.JToggleButtonOperator; +import org.netbeans.jemmy.operators.JTreeOperator; +import org.netbeans.jemmy.operators.JTreeOperator.NoSuchPathException; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferencesException; import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo; @@ -290,6 +293,16 @@ public class AutopsyTestCases { } + public void testExpandDataSourcesTree() { + logger.info("Data Sources Node"); + MainWindowOperator mwo = MainWindowOperator.getDefault(); + JTreeOperator jto = new JTreeOperator(mwo, "Data Sources"); + String [] nodeNames = {"Data Sources"}; + TreePath tp = jto.findPath(nodeNames); + expandNodes(jto, tp); + screenshot("Data Sources Tree"); + } + public void testGenerateReportToolbar() { logger.info("Generate Report Toolbars"); MainWindowOperator mwo = MainWindowOperator.getDefault(); @@ -380,4 +393,15 @@ public class AutopsyTestCases { logger.log(Level.SEVERE, "Error saving messaging service connection info", ex); //NON-NLS } } + + private void expandNodes (JTreeOperator jto, TreePath tp) { + try { + jto.expandPath(tp); + for (TreePath t : jto.getChildPaths(tp)) { + expandNodes(jto, t); + } + } catch (NoSuchPathException ne) { + logger.log(Level.SEVERE, "Error expanding tree path", ne); + } + } } diff --git a/Testing/test/qa-functional/src/org/sleuthkit/autopsy/testing/RegressionTest.java b/Testing/test/qa-functional/src/org/sleuthkit/autopsy/testing/RegressionTest.java index 674395e0f1..0518da865b 100755 --- a/Testing/test/qa-functional/src/org/sleuthkit/autopsy/testing/RegressionTest.java +++ b/Testing/test/qa-functional/src/org/sleuthkit/autopsy/testing/RegressionTest.java @@ -69,6 +69,7 @@ public class RegressionTest extends TestCase { "testConfigureSearch", "testAddSourceWizard1", "testIngest", + "testExpandDataSourcesTree", //After do ingest, before generate report, we expand Data Sources node "testGenerateReportToolbar", "testGenerateReportButton"); } @@ -83,6 +84,7 @@ public class RegressionTest extends TestCase { "testConfigureSearch", "testAddSourceWizard1", "testIngest", + "testExpandDataSourcesTree", "testGenerateReportToolbar", "testGenerateReportButton"); } @@ -147,6 +149,9 @@ public class RegressionTest extends TestCase { autopsyTests.testIngest(); } + public void testExpandDataSourcesTree() { + autopsyTests.testExpandDataSourcesTree(); + } public void testGenerateReportToolbar() { autopsyTests.testGenerateReportToolbar(); } diff --git a/nbproject/project.properties b/nbproject/project.properties index da174330bb..ac6c347444 100755 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -6,8 +6,8 @@ app.name=${branding.token} ### if left unset, version will default to today's date app.version=4.5.0 ### build.type must be one of: DEVELOPMENT, RELEASE -build.type=RELEASE -#build.type=DEVELOPMENT +#build.type=RELEASE +build.type=DEVELOPMENT project.org.netbeans.progress=org-netbeans-api-progress project.org.sleuthkit.autopsy.experimental=Experimental