diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index 60db8edaf8..ba8c4f867a 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -4,7 +4,6 @@ CTL_CaseCloseAct=Close Case CTL_CaseNewAction=New Case CTL_CasePropertiesAction=Case Properties CTL_CaseDeleteAction=Delete Case -CTL_CaseOpenAction=Open Case Menu/Case/OpenRecentCase=Open Recent Case CTL_CaseDeleteAction=Delete Case OpenIDE-Module-Name=Case @@ -210,14 +209,8 @@ CasePropertiesPanel.lbDbName.text=Database Name: CasePropertiesPanel.lbDbType.text=Case Type: CasePropertiesPanel.caseNumberLabel.text=Case Number: LocalDiskPanel.changeDatabasePathCheckbox.text=Update case to use VHD file upon completion -CueBannerPanel.openMultiUserCaseButton.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.openMultiUserCaseLabel.text=Open Multi-User Case ReviewModeCasePanel.cannotOpenCase=Cannot Open Case ReviewModeCasePanel.casePathNotFound=Case path not found ReviewModeCasePanel.caseIsLocked=Single-user case is locked. @@ -239,3 +232,8 @@ MultiUserCasesPanel.bnRefresh.text=&Refresh MultiUserCasesPanel.bnOpen.text=&Open MultiUserCasesPanel.rbGroupLabel.text=Show cases accessed in the last 10: MultiUserCasesPanel.rbMonths.text=Months +CueBannerPanel.newCaseLabel.text=New Case +CueBannerPanel.openCaseButton.text= +CueBannerPanel.openCaseLabel.text=Open Case +MultiUserCasesPanel.bnOpenSingleUserCase.text=Open Single-User Case... +CueBannerPanel.newCaseButton.text= diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties index de333e790f..8b7bfae3eb 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties @@ -190,10 +190,10 @@ CasePropertiesPanel.caseNumberLabel.text=\u30b1\u30fc\u30b9\u756a\u53f7\uff1a CasePropertiesPanel.examinerLabel.text=\u8abf\u67fb\u62c5\u5f53\u8005\uff1a OptionalCasePropertiesPanel.examinerLabel.text=\u8abf\u67fb\u62c5\u5f53\u8005\uff1a OptionalCasePropertiesPanel.caseDisplayNameLabel.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 OpenMultiUserCasePanel.openButton.text=\u958b\u304f OpenMultiUserCasePanel.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb OpenMultiUserCasePanel.jLabel1.text=\u6700\u8fd1\u958b\u3044\u305f\u30d5\u30a1\u30a4\u30eb +CueBannerPanel.newCaseLabel.text=\u65b0\u898f\u30b1\u30fc\u30b9\u3092\u4f5c\u6210 +CueBannerPanel.openCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index e2e58c5b5a..3709654e24 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -1610,11 +1610,8 @@ public class Case { * will be created if it doesn't already exist; if it * exists, it is ASSUMED it was created by calling * createCaseDirectory. - * @param caseDisplayName The display name of case, which may be changed - * later by the user. - * @param caseNumber The case number, can be the empty string. - * @param examiner The examiner to associate with the case, can be - * the empty string. + * @param caseDetails Contains details of the case, such as examiner, display name, etc + * */ private Case(CaseType caseType, String caseDir, CaseDetails caseDetails) { metadata = new CaseMetadata(caseType, caseDir, displayNameToUniqueName(caseDetails.getCaseDisplayName()), caseDetails); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java index e4ed92d897..a8e18da650 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenAction.java @@ -24,17 +24,22 @@ import java.awt.event.ActionListener; import java.io.File; import java.util.concurrent.ExecutionException; import java.util.logging.Level; +import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.SwingWorker; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileNameExtensionFilter; +import org.openide.awt.ActionID; +import org.openide.awt.ActionReference; +import org.openide.awt.ActionRegistration; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; import org.openide.util.lookup.ServiceProvider; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.actions.IngestRunningCheck; +import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.Version; @@ -46,21 +51,26 @@ import org.sleuthkit.autopsy.coreutils.Version; * * This action should only be invoked in the event dispatch thread (EDT). */ +@ActionID(category = "Case", id = "org.sleuthkit.autopsy.casemodule.CaseOpenAction") +@ActionReference(path = "Menu/Case", position = 102) +@ActionRegistration(displayName = "#CTL_CaseOpenAction", lazy = false) +@NbBundle.Messages({"CTL_CaseOpenAction=Open Case"}) @ServiceProvider(service = CaseOpenAction.class) public final class CaseOpenAction extends CallableSystemAction implements ActionListener { private static final long serialVersionUID = 1L; + private static final String DISPLAY_NAME = Bundle.CTL_CaseOpenAction(); private static final String PROP_BASECASE = "LBL_BaseCase_PATH"; //NON-NLS - private static final Logger logger = Logger.getLogger(CaseOpenAction.class.getName()); + private static final Logger LOGGER = Logger.getLogger(CaseOpenAction.class.getName()); + private static JDialog multiUserCaseWindow; private final JFileChooser fileChooser = new JFileChooser(); private final FileFilter caseMetadataFileFilter; /** * Constructs the action associated with the Case/Open Case menu item via - * the layer.xml file, a toolbar button, and the Create New Case button of - * the start up window that allows a user to open a case. It opens an - * existing case. - * + * the layer.xml file, a toolbar button, and the Open Case button of the + * start up window that allows a user to open a case. It opens an existing + * case. */ public CaseOpenAction() { caseMetadataFileFilter = new FileNameExtensionFilter(NbBundle.getMessage(CaseOpenAction.class, "CaseOpenAction.autFilter.title", Version.getName(), CaseMetadata.getFileExtension()), CaseMetadata.getFileExtension().substring(1)); @@ -74,13 +84,11 @@ public final class CaseOpenAction extends CallableSystemAction implements Action } /** - * Pops up a file chooser to allow the user to select a case metadata file - * (.aut file) and attempts to open the case described by the file. - * - * @param e The action event. + * Open the case selection window to allow the user to select a case + * metadata file (.aut file). Upon confirming the selection, it will attempt + * to open the case described by the file. */ - @Override - public void actionPerformed(ActionEvent e) { + void openCaseSelectionWindow() { String optionsDlgTitle = NbBundle.getMessage(Case.class, "CloseCaseWhileIngesting.Warning.title"); String optionsDlgMessage = NbBundle.getMessage(Case.class, "CloseCaseWhileIngesting.Warning"); if (IngestRunningCheck.checkAndConfirmProceed(optionsDlgTitle, optionsDlgMessage)) { @@ -95,6 +103,13 @@ public final class CaseOpenAction extends CallableSystemAction implements Action */ StartupWindowProvider.getInstance().close(); + /* + * Close the Open Multi-User Case window, if it is open. + */ + if (multiUserCaseWindow != null) { + multiUserCaseWindow.setVisible(false); + } + /* * Try to open the case associated with the case metadata file * the user selected. @@ -117,7 +132,7 @@ public final class CaseOpenAction extends CallableSystemAction implements Action get(); } catch (InterruptedException | ExecutionException ex) { if (ex instanceof InterruptedException || (null != ex.getCause() && !(ex.getCause() instanceof CaseActionCancelledException))) { - logger.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", path), ex); //NON-NLS + LOGGER.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", path), ex); //NON-NLS JOptionPane.showMessageDialog( WindowManager.getDefault().getMainWindow(), ex.getCause().getMessage(), //get the message of the wrapped exception @@ -134,6 +149,29 @@ public final class CaseOpenAction extends CallableSystemAction implements Action } } + /** + * Pops up either the case selection window or the Open Multi-User Case + * window, depending on the multi-user case settings. + * + * @param e The action event. + */ + @Override + public void actionPerformed(ActionEvent e) { + if (UserPreferences.getIsMultiUserModeEnabled()) { + WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + if (multiUserCaseWindow == null) { + multiUserCaseWindow = MultiUserCasesDialog.getInstance(); + } + multiUserCaseWindow.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); + multiUserCaseWindow.setVisible(true); + + WindowManager.getDefault().getMainWindow().setCursor(null); + } else { + openCaseSelectionWindow(); + } + } + @Override public void performAction() { actionPerformed(null); @@ -141,7 +179,7 @@ public final class CaseOpenAction extends CallableSystemAction implements Action @Override public String getName() { - return NbBundle.getMessage(CaseOpenAction.class, "CTL_CaseOpenAction"); + return DISPLAY_NAME; } @Override diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenMultiUserAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenMultiUserAction.java deleted file mode 100755 index 9e181bf23c..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseOpenMultiUserAction.java +++ /dev/null @@ -1,97 +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.casemodule; - -import java.awt.Cursor; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import javax.swing.JDialog; -import org.openide.awt.ActionID; -import org.openide.awt.ActionReference; -import org.openide.awt.ActionRegistration; -import org.openide.util.HelpCtx; -import org.openide.util.NbBundle; -import org.openide.util.actions.CallableSystemAction; -import org.openide.windows.WindowManager; -import org.sleuthkit.autopsy.core.UserPreferences; - -/** - * The action associated with the Open Multi-User Case menu item via the - * layer.xml file. - * - * This action should only be invoked in the event dispatch thread (EDT). - */ -@ActionID(category = "Case", id = "org.sleuthkit.autopsy.casemodule.CaseOpenMultiUserAction") -@ActionReference(path = "Menu/Case", position = 102) -@ActionRegistration(displayName = "#CTL_CaseOpenMultiUserAction", lazy = false) -@NbBundle.Messages({"CTL_CaseOpenMultiUserAction=Open Multi-User Case"}) -public final class CaseOpenMultiUserAction extends CallableSystemAction implements ActionListener { - - private static final long serialVersionUID = 1L; - private static JDialog multiUserCaseWindow; - - private static final String DISPLAY_NAME = Bundle.CTL_CaseOpenMultiUserAction(); - - public CaseOpenMultiUserAction() { - } - - @Override - public boolean isEnabled() { - return UserPreferences.getIsMultiUserModeEnabled(); - } - - /** - * Pops up a case selection panel to allow the user to select a multi-user - * case to open. - * - * @param event The action event. - */ - @Override - public void actionPerformed(ActionEvent event) { - WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - if (multiUserCaseWindow == null) { - multiUserCaseWindow = MultiUserCasesDialog.getInstance(); - } - multiUserCaseWindow.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); - multiUserCaseWindow.setVisible(true); - - WindowManager.getDefault().getMainWindow().setCursor(null); - } - - @Override - public void performAction() { - actionPerformed(null); - } - - @Override - public String getName() { - return DISPLAY_NAME; - } - - @Override - public HelpCtx getHelpCtx() { - return HelpCtx.DEFAULT_HELP; - } - - @Override - public boolean asynchronous() { - return false; // run on edt - } -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form index 0d6525db8b..8e14feba22 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form @@ -18,29 +18,31 @@ + @@ -48,37 +50,31 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - + @@ -97,13 +93,13 @@ - + - + @@ -115,7 +111,7 @@ - + @@ -139,15 +135,15 @@ - + - + - + @@ -163,13 +159,13 @@ - + - + @@ -184,18 +180,18 @@ - + - + - + - + @@ -216,41 +212,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java index 933b0de554..0bef140c6d 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java @@ -31,7 +31,6 @@ import javax.swing.KeyStroke; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; -import org.sleuthkit.autopsy.core.UserPreferences; /* * The panel in the default Autopsy startup window. @@ -107,10 +106,6 @@ public class CueBannerPanel extends javax.swing.JPanel { boolean enableOpenRecentCaseButton = (RecentCases.getInstance().getTotalRecentCases() > 0); openRecentCaseButton.setEnabled(enableOpenRecentCaseButton); openRecentCaseLabel.setEnabled(enableOpenRecentCaseButton); - - boolean enableOpenMultiUserCaseButton = UserPreferences.getIsMultiUserModeEnabled(); - openMultiUserCaseButton.setEnabled(enableOpenMultiUserCaseButton); - openMultiUserCaseLabel.setEnabled(enableOpenMultiUserCaseButton); } /** @@ -124,29 +119,27 @@ public class CueBannerPanel extends javax.swing.JPanel { autopsyLogo = new javax.swing.JLabel(); this.autopsyLogo.setText(""); - createNewCaseButton = new javax.swing.JButton(); + newCaseButton = new javax.swing.JButton(); openRecentCaseButton = new javax.swing.JButton(); - createNewCaseLabel = new javax.swing.JLabel(); + newCaseLabel = new javax.swing.JLabel(); openRecentCaseLabel = new javax.swing.JLabel(); - openExistingCaseButton = new javax.swing.JButton(); - openExistingCaseLabel = new javax.swing.JLabel(); + openCaseButton = new javax.swing.JButton(); + openCaseLabel = new javax.swing.JLabel(); closeButton = new javax.swing.JButton(); jSeparator1 = new javax.swing.JSeparator(); - openMultiUserCaseButton = new javax.swing.JButton(); - openMultiUserCaseLabel = new javax.swing.JLabel(); autopsyLogo.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/welcome_logo.png"))); // NOI18N autopsyLogo.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.autopsyLogo.text")); // NOI18N - createNewCaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case.png"))); // NOI18N - createNewCaseButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.createNewCaseButton.text")); // NOI18N - createNewCaseButton.setBorder(null); - createNewCaseButton.setBorderPainted(false); - createNewCaseButton.setContentAreaFilled(false); - createNewCaseButton.setPreferredSize(new java.awt.Dimension(64, 64)); - createNewCaseButton.addActionListener(new java.awt.event.ActionListener() { + newCaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case.png"))); // NOI18N + newCaseButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.newCaseButton.text")); // NOI18N + newCaseButton.setBorder(null); + newCaseButton.setBorderPainted(false); + newCaseButton.setContentAreaFilled(false); + newCaseButton.setPreferredSize(new java.awt.Dimension(64, 64)); + newCaseButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - createNewCaseButtonActionPerformed(evt); + newCaseButtonActionPerformed(evt); } }); @@ -162,113 +155,95 @@ public class CueBannerPanel extends javax.swing.JPanel { } }); - createNewCaseLabel.setFont(createNewCaseLabel.getFont().deriveFont(createNewCaseLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13)); - createNewCaseLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.createNewCaseLabel.text")); // NOI18N + newCaseLabel.setFont(newCaseLabel.getFont().deriveFont(newCaseLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13)); + newCaseLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.newCaseLabel.text")); // NOI18N openRecentCaseLabel.setFont(openRecentCaseLabel.getFont().deriveFont(openRecentCaseLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13)); openRecentCaseLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openRecentCaseLabel.text")); // NOI18N - openExistingCaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"))); // NOI18N - openExistingCaseButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openExistingCaseButton.text")); // NOI18N - openExistingCaseButton.setBorder(null); - openExistingCaseButton.setBorderPainted(false); - openExistingCaseButton.setContentAreaFilled(false); - openExistingCaseButton.setMargin(new java.awt.Insets(1, 1, 1, 1)); - openExistingCaseButton.setPreferredSize(new java.awt.Dimension(64, 64)); - openExistingCaseButton.addActionListener(new java.awt.event.ActionListener() { + openCaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"))); // NOI18N + openCaseButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openCaseButton.text")); // NOI18N + openCaseButton.setBorder(null); + openCaseButton.setBorderPainted(false); + openCaseButton.setContentAreaFilled(false); + openCaseButton.setMargin(new java.awt.Insets(1, 1, 1, 1)); + openCaseButton.setPreferredSize(new java.awt.Dimension(64, 64)); + openCaseButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - openExistingCaseButtonActionPerformed(evt); + openCaseButtonActionPerformed(evt); } }); - openExistingCaseLabel.setFont(openExistingCaseLabel.getFont().deriveFont(openExistingCaseLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13)); - openExistingCaseLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openExistingCaseLabel.text")); // NOI18N + openCaseLabel.setFont(openCaseLabel.getFont().deriveFont(openCaseLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13)); + openCaseLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openCaseLabel.text")); // NOI18N closeButton.setFont(closeButton.getFont().deriveFont(closeButton.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); closeButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.closeButton.text")); // NOI18N jSeparator1.setOrientation(javax.swing.SwingConstants.VERTICAL); - openMultiUserCaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"))); // NOI18N - openMultiUserCaseButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openMultiUserCaseButton.text")); // NOI18N - openMultiUserCaseButton.setBorder(null); - openMultiUserCaseButton.setBorderPainted(false); - openMultiUserCaseButton.setContentAreaFilled(false); - openMultiUserCaseButton.setMargin(new java.awt.Insets(1, 1, 1, 1)); - openMultiUserCaseButton.setPreferredSize(new java.awt.Dimension(64, 64)); - openMultiUserCaseButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - openMultiUserCaseButtonActionPerformed(evt); - } - }); - - openMultiUserCaseLabel.setFont(openMultiUserCaseLabel.getFont().deriveFont(openMultiUserCaseLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13)); - openMultiUserCaseLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openMultiUserCaseLabel.text")); // NOI18N - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() + .addComponent(autopsyLogo) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 5, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(autopsyLogo) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 5, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) - .addComponent(createNewCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(openRecentCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(openExistingCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(openMultiUserCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(createNewCaseLabel) + .addComponent(newCaseLabel) .addComponent(openRecentCaseLabel) - .addComponent(openExistingCaseLabel) - .addComponent(openMultiUserCaseLabel))) - .addComponent(closeButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap()) + .addComponent(openCaseLabel)) + .addGap(18, 18, 18)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 139, Short.MAX_VALUE) + .addComponent(closeButton, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) - .addComponent(createNewCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(createNewCaseLabel)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) - .addComponent(openRecentCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(openRecentCaseLabel)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) - .addComponent(openExistingCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(openExistingCaseLabel)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(openMultiUserCaseButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(openMultiUserCaseLabel) - .addGap(20, 20, 20)))) - .addComponent(jSeparator1) - .addComponent(autopsyLogo, javax.swing.GroupLayout.PREFERRED_SIZE, 257, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 10, Short.MAX_VALUE) - .addComponent(closeButton) - .addContainerGap()) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(autopsyLogo, javax.swing.GroupLayout.PREFERRED_SIZE, 257, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) + .addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(newCaseLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) + .addComponent(openRecentCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(openRecentCaseLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) + .addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(openCaseLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(closeButton)) + .addComponent(jSeparator1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 260, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); }// //GEN-END:initComponents - private void createNewCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createNewCaseButtonActionPerformed + private void newCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newCaseButtonActionPerformed Lookup.getDefault().lookup(CaseNewActionInterface.class).actionPerformed(evt); - }//GEN-LAST:event_createNewCaseButtonActionPerformed + }//GEN-LAST:event_newCaseButtonActionPerformed - private void openExistingCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openExistingCaseButtonActionPerformed + private void openCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openCaseButtonActionPerformed + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); Lookup.getDefault().lookup(CaseOpenAction.class).actionPerformed(evt); - }//GEN-LAST:event_openExistingCaseButtonActionPerformed + setCursor(null); + }//GEN-LAST:event_openCaseButtonActionPerformed private void openRecentCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openRecentCaseButtonActionPerformed recentCasesWindow.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); @@ -276,27 +251,14 @@ public class CueBannerPanel extends javax.swing.JPanel { recentCasesWindow.setVisible(true); }//GEN-LAST:event_openRecentCaseButtonActionPerformed - private void openMultiUserCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openMultiUserCaseButtonActionPerformed - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - MultiUserCasesDialog multiUserCaseWindow = MultiUserCasesDialog.getInstance(); - multiUserCaseWindow.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); - multiUserCaseWindow.setVisible(true); - - setCursor(null); - - }//GEN-LAST:event_openMultiUserCaseButtonActionPerformed - // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel autopsyLogo; private javax.swing.JButton closeButton; - private javax.swing.JButton createNewCaseButton; - private javax.swing.JLabel createNewCaseLabel; private javax.swing.JSeparator jSeparator1; - private javax.swing.JButton openMultiUserCaseButton; - private javax.swing.JLabel openMultiUserCaseLabel; - private javax.swing.JButton openExistingCaseButton; - private javax.swing.JLabel openExistingCaseLabel; + private javax.swing.JButton newCaseButton; + private javax.swing.JLabel newCaseLabel; + private javax.swing.JButton openCaseButton; + private javax.swing.JLabel openCaseLabel; private javax.swing.JButton openRecentCaseButton; private javax.swing.JLabel openRecentCaseLabel; // End of variables declaration//GEN-END:variables diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.form index 18d99fa0dc..153f81561f 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.form @@ -7,6 +7,9 @@ + + + @@ -30,8 +33,10 @@ + + - + @@ -41,9 +46,8 @@ - + - @@ -60,9 +64,9 @@ + - @@ -72,6 +76,7 @@ + @@ -224,5 +229,15 @@ + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java index d0676e1966..018def8ad3 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java @@ -29,12 +29,12 @@ import java.util.List; import java.util.logging.Level; import javax.swing.JDialog; import javax.swing.JOptionPane; -import javax.swing.RowSorter; import javax.swing.SortOrder; import javax.swing.event.ListSelectionEvent; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; +import org.openide.util.Lookup; import org.sleuthkit.autopsy.casemodule.MultiUserCaseManager.MultiUserCase; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coreutils.Logger; @@ -369,8 +369,10 @@ final class MultiUserCasesPanel extends javax.swing.JPanel { rbWeeks = new javax.swing.JRadioButton(); rbMonths = new javax.swing.JRadioButton(); rbGroupLabel = new javax.swing.JLabel(); + bnOpenSingleUserCase = new javax.swing.JButton(); setName("Completed Cases"); // NOI18N + setPreferredSize(new java.awt.Dimension(960, 485)); org.openide.awt.Mnemonics.setLocalizedText(bnOpen, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnOpen.text")); // NOI18N bnOpen.setEnabled(false); @@ -458,6 +460,13 @@ final class MultiUserCasesPanel extends javax.swing.JPanel { rbGroupLabel.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(rbGroupLabel, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.rbGroupLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(bnOpenSingleUserCase, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnOpenSingleUserCase.text")); // NOI18N + bnOpenSingleUserCase.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnOpenSingleUserCaseActionPerformed(evt); + } + }); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -469,8 +478,10 @@ final class MultiUserCasesPanel extends javax.swing.JPanel { .addGap(4, 4, 4) .addComponent(bnOpen, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(bnOpenSingleUserCase) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(bnShowLog) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 13, Short.MAX_VALUE) .addComponent(rbGroupLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(rbDays) @@ -480,9 +491,8 @@ final class MultiUserCasesPanel extends javax.swing.JPanel { .addComponent(rbMonths) .addGap(0, 0, 0) .addComponent(panelFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(bnRefresh) - .addGap(4, 4, 4)) + .addGap(14, 14, 14) + .addComponent(bnRefresh)) .addComponent(scrollPaneTable)) .addContainerGap()) ); @@ -495,15 +505,16 @@ final class MultiUserCasesPanel extends javax.swing.JPanel { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(bnOpen) + .addComponent(bnOpenSingleUserCase) .addComponent(bnShowLog)) - .addComponent(bnRefresh) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(rbDays) .addComponent(rbWeeks) .addComponent(rbMonths) .addComponent(rbGroupLabel)) - .addComponent(panelFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(panelFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(bnRefresh, javax.swing.GroupLayout.Alignment.TRAILING)) .addGap(0, 0, 0)) ); }// //GEN-END:initComponents @@ -586,8 +597,13 @@ final class MultiUserCasesPanel extends javax.swing.JPanel { } }//GEN-LAST:event_casesTableMouseClicked + private void bnOpenSingleUserCaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOpenSingleUserCaseActionPerformed + Lookup.getDefault().lookup(CaseOpenAction.class).openCaseSelectionWindow(); + }//GEN-LAST:event_bnOpenSingleUserCaseActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton bnOpen; + private javax.swing.JButton bnOpenSingleUserCase; private javax.swing.JButton bnRefresh; private javax.swing.JButton bnShowLog; private javax.swing.JTable casesTable; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalSet.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalSet.java index a434afb32b..722c87407c 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalSet.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalSet.java @@ -75,6 +75,7 @@ public class EamGlobalSet { * @param version * @param knownStatus * @param isReadOnly + * @param type */ public EamGlobalSet( int orgID, @@ -164,7 +165,7 @@ public class EamGlobalSet { } /** - * @param knownStatus the known status to set + * @param fileKnownStatus the known status to set */ public void setFileKnownStatus(TskData.FileKnown fileKnownStatus) { this.fileKnownStatus = fileKnownStatus; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java index 98396eef9c..13c4fb00a7 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java @@ -645,8 +645,9 @@ public class SqliteEamDb extends AbstractSqlEamDb { /** * Check if the given hash is in a specific reference set - * @param hash + * @param value * @param referenceSetID + * @param correlationTypeID * @return true if the hash is found in the reference set */ @Override diff --git a/Core/src/org/sleuthkit/autopsy/core/layer.xml b/Core/src/org/sleuthkit/autopsy/core/layer.xml index e06fc9ac45..465529fa9f 100755 --- a/Core/src/org/sleuthkit/autopsy/core/layer.xml +++ b/Core/src/org/sleuthkit/autopsy/core/layer.xml @@ -49,12 +49,6 @@ - - - - - - @@ -152,12 +146,8 @@ - - - - - + @@ -165,11 +155,11 @@ - + - + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form index 00881c2ca7..c1fc384c5e 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form @@ -2,9 +2,11 @@
- + - + + + @@ -22,10 +24,7 @@ - - - - + @@ -69,7 +68,7 @@ - + @@ -92,13 +91,17 @@ - - - + + + + + + - + + @@ -109,42 +112,31 @@ - - - - - - - - - - - + + + + - + + + + + + + + + + - - - - - - - - - - - - - @@ -179,6 +171,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -276,7 +304,7 @@ - + @@ -292,7 +320,7 @@ - + @@ -369,7 +397,7 @@ - + @@ -382,7 +410,7 @@ - + @@ -427,7 +455,7 @@ - + @@ -442,7 +470,7 @@ - + @@ -503,7 +531,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java index a62ae4400c..a9640f005e 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java @@ -34,6 +34,8 @@ import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JFileChooser; import javax.swing.JOptionPane; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import org.netbeans.spi.options.OptionsPanelController; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; @@ -58,35 +60,45 @@ import org.sleuthkit.autopsy.report.ReportBranding; "AutopsyOptionsPanel.maxMemoryLabel.text=Maximum JVM Memory:", "AutopsyOptionsPanel.maxMemoryUnitsLabel.text=GB", "AutopsyOptionsPanel.runtimePanel.border.title=Runtime", - "AutopsyOptionsPanel.invalidReasonLabel.not64BitInstall.text=JVM memory settings only enabled for 64 bit version", - "AutopsyOptionsPanel.invalidReasonLabel.noValueEntered.text=No value entered", - "AutopsyOptionsPanel.invalidReasonLabel.invalidCharacters.text=Invalid characters, value must be a positive integer", + "AutopsyOptionsPanel.memFieldValidationLabel.not64BitInstall.text=JVM memory settings only enabled for 64 bit version", + "AutopsyOptionsPanel.memFieldValidationLabel.noValueEntered.text=No value entered", + "AutopsyOptionsPanel.memFieldValidationLabel.invalidCharacters.text=Invalid characters, value must be a positive integer", "# {0} - minimumMemory", - "AutopsyOptionsPanel.invalidReasonLabel.underMinMemory.text=Value must be at least {0}GB", + "AutopsyOptionsPanel.memFieldValidationLabel.underMinMemory.text=Value must be at least {0}GB", "# {0} - systemMemory", - "AutopsyOptionsPanel.invalidReasonLabel.overMaxMemory.text=Value must be less than the total system memory of {0}GB", - "AutopsyOptionsPanel.invalidReasonLabel.developerMode.text=Memory settings are not available while running in developer mode"}) + "AutopsyOptionsPanel.memFieldValidationLabel.overMaxMemory.text=Value must be less than the total system memory of {0}GB", + "AutopsyOptionsPanel.memFieldValidationLabel.developerMode.text=Memory settings are not available while running in developer mode", + "AutopsyOptionsPanel.defaultLogoRB.text=Use default", + "AutopsyOptionsPanel.specifyLogoRB.text=Specify a logo", + "AutopsyOptionsPanel.browseLogosButton.text=Browse", + "AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.invalidPath.text=Path is not valid.", + "AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.pathNotSet.text=Agency logo path must be set." +}) final class AutopsyOptionsPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; - private final JFileChooser fc; + private final JFileChooser fileChooser; + private final TextFieldListener textFieldListener; private static final String ETC_FOLDER_NAME = "etc"; private static final String CONFIG_FILE_EXTENSION = ".conf"; private static final long ONE_BILLION = 1000000000L; //used to roughly convert system memory from bytes to gigabytes private static final long MEGA_IN_GIGA = 1024; //used to convert memory settings saved as megabytes to gigabytes private static final int MIN_MEMORY_IN_GB = 2; //the enforced minimum memory in gigabytes - private static final Logger logger = Logger.getLogger(AutopsyOptionsPanel.class.getName()); + private static final Logger LOGGER = Logger.getLogger(AutopsyOptionsPanel.class.getName()); private String initialMemValue = Long.toString(Runtime.getRuntime().maxMemory() / ONE_BILLION); + /** + * Instantiate the Autopsy options panel. + */ AutopsyOptionsPanel() { initComponents(); - fc = new JFileChooser(); - fc.setFileSelectionMode(JFileChooser.FILES_ONLY); - fc.setMultiSelectionEnabled(false); - fc.setAcceptAllFileFilterUsed(false); - fc.setFileFilter(new GeneralFilter(GeneralFilter.GRAPHIC_IMAGE_EXTS, GeneralFilter.GRAPHIC_IMG_DECR)); - if (!PlatformUtil.is64BitJVM() || Version.getBuildType() == Version.Type.DEVELOPMENT) { + fileChooser = new JFileChooser(); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setAcceptAllFileFilterUsed(false); + fileChooser.setFileFilter(new GeneralFilter(GeneralFilter.GRAPHIC_IMAGE_EXTS, GeneralFilter.GRAPHIC_IMG_DECR)); + if (!PlatformUtil.is64BitJVM() || Version.getBuildType() == Version.Type.DEVELOPMENT) { //32 bit JVM has a max heap size of 1.4 gb to 4 gb depending on OS //So disabling the setting of heap size when the JVM is not 64 bit //Is the safest course of action @@ -94,6 +106,9 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { memField.setEnabled(false); } systemMemoryTotal.setText(Long.toString(getSystemMemoryInGB())); + + textFieldListener = new TextFieldListener(); + agencyLogoPathField.getDocument().addDocumentListener(textFieldListener); } /** @@ -247,7 +262,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { try { lines = Files.readAllLines(filePath, charset); } catch (IOException e) { - logger.log(Level.SEVERE, "Error reading config file contents. {}", configFile.getAbsolutePath()); + LOGGER.log(Level.SEVERE, "Error reading config file contents. {}", configFile.getAbsolutePath()); } } return lines; @@ -273,6 +288,9 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { return new String[]{}; } + /** + * Load the saved user preferences. + */ void load() { boolean keepPreferredViewer = UserPreferences.keepPreferredContentViewer(); keepCurrentViewerRB.setSelected(keepPreferredViewer); @@ -285,23 +303,36 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { useLocalTimeRB.setSelected(useLocalTime); useGMTTimeRB.setSelected(!useLocalTime); String path = ModuleSettings.getConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP); + boolean useDefault = (path == null || path.isEmpty()); + defaultLogoRB.setSelected(useDefault); + specifyLogoRB.setSelected(!useDefault); + agencyLogoPathField.setEnabled(!useDefault); + browseLogosButton.setEnabled(!useDefault); try { updateAgencyLogo(path); } catch (IOException ex) { - logger.log(Level.WARNING, "Error loading image from previously saved agency logo path", ex); + LOGGER.log(Level.WARNING, "Error loading image from previously saved agency logo path", ex); } if (memField.isEnabled()) { try { initialMemValue = Long.toString(getCurrentJvmMaxMemoryInGB()); } catch (IOException ex) { - logger.log(Level.SEVERE, "Can't read current Jvm max memory setting from file", ex); + LOGGER.log(Level.SEVERE, "Can't read current Jvm max memory setting from file", ex); memField.setEnabled(false); } memField.setText(initialMemValue); } - isMemFieldValid(); //ensure the error message is up to date + + valid(); //ensure the error messages are up to date } + /** + * Update the agency logo with the image specified by the path. + * + * @param path The path to the image. + * + * @throws IOException Thrown when there is a problem reading the file. + */ private void updateAgencyLogo(String path) throws IOException { agencyLogoPathField.setText(path); ImageIcon agencyLogoIcon = new ImageIcon(); @@ -321,6 +352,9 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { agencyLogoPreview.repaint(); } + /** + * Store the current user preferences. + */ void store() { UserPreferences.setKeepPreferredContentViewer(keepCurrentViewerRB.isSelected()); UserPreferences.setHideKnownFilesInDataSourcesTree(dataSourcesHideKnownCB.isSelected()); @@ -333,18 +367,124 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { if (file.exists()) { ModuleSettings.setConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP, agencyLogoPathField.getText()); } + } else { + ModuleSettings.setConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP, ""); } if (memField.isEnabled()) { //if the field could of been changed we need to try and save it try { writeEtcConfFile(); } catch (IOException ex) { - logger.log(Level.WARNING, "Unable to save config file to " + PlatformUtil.getUserDirectory() + "\\" + ETC_FOLDER_NAME, ex); + LOGGER.log(Level.WARNING, "Unable to save config file to " + PlatformUtil.getUserDirectory() + "\\" + ETC_FOLDER_NAME, ex); } } } + /** + * Checks to see if the memory and agency logo field inputs are valid. + * + * @return True if valid; false otherwise. + */ boolean valid() { - return isMemFieldValid(); + boolean valid = true; + + if (!isAgencyLogoPathValid()) { + valid = false; + } + if (!isMemFieldValid()) { + valid = false; + } + + return valid; + } + + /** + * Validates the agency logo path to ensure it is valid. + * + * @return True if the default logo is being used or if the path is valid; + * otherwise false. + */ + boolean isAgencyLogoPathValid() { + boolean valid = true; + + if (defaultLogoRB.isSelected()) { + agencyLogoPathFieldValidationLabel.setText(""); + } else { + String agencyLogoPath = agencyLogoPathField.getText(); + if (agencyLogoPath.isEmpty()) { + agencyLogoPathFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_agencyLogoPathFieldValidationLabel_pathNotSet_text()); + valid = false; + } else { + File file = new File(agencyLogoPathField.getText()); + if (file.exists() && file.isFile()) { + agencyLogoPathFieldValidationLabel.setText(""); + } else { + agencyLogoPathFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_agencyLogoPathFieldValidationLabel_invalidPath_text()); + valid = false; + } + } + } + + return valid; + } + + /** + * Checks that if the mem field is enabled it has a valid value. + * + * @return true if the memfield is valid false if it is not + */ + private boolean isMemFieldValid() { + String memText = memField.getText(); + memFieldValidationLabel.setText(""); + if (!PlatformUtil.is64BitJVM()) { + memFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_memFieldValidationLabel_not64BitInstall_text()); + //the panel should be valid when it is a 32 bit jvm because the memfield will be disabled. + return true; + } + if (Version.getBuildType() == Version.Type.DEVELOPMENT) { + memFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_memFieldValidationLabel_developerMode_text()); + //the panel should be valid when you are running in developer mode because the memfield will be disabled + return true; + } + if (memText.length() == 0) { + memFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_memFieldValidationLabel_noValueEntered_text()); + return false; + } + if (memText.replaceAll("[^\\d]", "").length() != memText.length()) { + memFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_memFieldValidationLabel_invalidCharacters_text()); + return false; + } + int parsedInt = Integer.parseInt(memText); + if (parsedInt < MIN_MEMORY_IN_GB) { + memFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_memFieldValidationLabel_underMinMemory_text(MIN_MEMORY_IN_GB)); + return false; + } + if (parsedInt >= getSystemMemoryInGB()) { + memFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_memFieldValidationLabel_overMaxMemory_text(getSystemMemoryInGB())); + return false; + } + return true; + } + + /** + * Listens for registered text fields that have changed and fires a + * PropertyChangeEvent accordingly. + */ + private class TextFieldListener implements DocumentListener { + + @Override + public void insertUpdate(DocumentEvent e) { + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + } + + @Override + public void removeUpdate(DocumentEvent e) { + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + } + + @Override + public void changedUpdate(DocumentEvent e) { + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + } } /** @@ -355,15 +495,18 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { // //GEN-BEGIN:initComponents private void initComponents() { - buttonGroup1 = new javax.swing.ButtonGroup(); - buttonGroup3 = new javax.swing.ButtonGroup(); + fileSelectionButtonGroup = new javax.swing.ButtonGroup(); + displayTimesButtonGroup = new javax.swing.ButtonGroup(); + logoSourceButtonGroup = new javax.swing.ButtonGroup(); jScrollPane1 = new javax.swing.JScrollPane(); jPanel1 = new javax.swing.JPanel(); logoPanel = new javax.swing.JPanel(); - agencyLogoImageLabel = new javax.swing.JLabel(); agencyLogoPathField = new javax.swing.JTextField(); browseLogosButton = new javax.swing.JButton(); agencyLogoPreview = new javax.swing.JLabel(); + defaultLogoRB = new javax.swing.JRadioButton(); + specifyLogoRB = new javax.swing.JRadioButton(); + agencyLogoPathFieldValidationLabel = new javax.swing.JLabel(); viewPanel = new javax.swing.JPanel(); jLabelSelectFile = new javax.swing.JLabel(); useBestViewerRB = new javax.swing.JRadioButton(); @@ -384,20 +527,14 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { systemMemoryTotal = new javax.swing.JLabel(); restartNecessaryWarning = new javax.swing.JLabel(); memField = new javax.swing.JTextField(); - invalidReasonLabel = new javax.swing.JLabel(); + memFieldValidationLabel = new javax.swing.JLabel(); maxMemoryUnitsLabel1 = new javax.swing.JLabel(); jScrollPane1.setBorder(null); logoPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.logoPanel.border.title"))); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(agencyLogoImageLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.agencyLogoImageLabel.text")); // NOI18N - - agencyLogoPathField.setEditable(false); - agencyLogoPathField.setBackground(new java.awt.Color(255, 255, 255)); agencyLogoPathField.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.agencyLogoPathField.text")); // NOI18N - agencyLogoPathField.setFocusable(false); - agencyLogoPathField.setRequestFocusEnabled(false); org.openide.awt.Mnemonics.setLocalizedText(browseLogosButton, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.browseLogosButton.text")); // NOI18N browseLogosButton.addActionListener(new java.awt.event.ActionListener() { @@ -413,6 +550,25 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { agencyLogoPreview.setMinimumSize(new java.awt.Dimension(64, 64)); agencyLogoPreview.setPreferredSize(new java.awt.Dimension(64, 64)); + logoSourceButtonGroup.add(defaultLogoRB); + org.openide.awt.Mnemonics.setLocalizedText(defaultLogoRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.defaultLogoRB.text")); // NOI18N + defaultLogoRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + defaultLogoRBActionPerformed(evt); + } + }); + + logoSourceButtonGroup.add(specifyLogoRB); + org.openide.awt.Mnemonics.setLocalizedText(specifyLogoRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.specifyLogoRB.text")); // NOI18N + specifyLogoRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + specifyLogoRBActionPerformed(evt); + } + }); + + agencyLogoPathFieldValidationLabel.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(agencyLogoPathFieldValidationLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text")); // NOI18N + javax.swing.GroupLayout logoPanelLayout = new javax.swing.GroupLayout(logoPanel); logoPanel.setLayout(logoPanelLayout); logoPanelLayout.setHorizontalGroup( @@ -420,12 +576,15 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, logoPanelLayout.createSequentialGroup() .addContainerGap() .addGroup(logoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(agencyLogoImageLabel) + .addComponent(specifyLogoRB, javax.swing.GroupLayout.PREFERRED_SIZE, 93, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(defaultLogoRB, javax.swing.GroupLayout.PREFERRED_SIZE, 81, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(logoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(logoPanelLayout.createSequentialGroup() - .addGap(10, 10, 10) .addComponent(agencyLogoPathField, javax.swing.GroupLayout.PREFERRED_SIZE, 259, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(browseLogosButton))) + .addComponent(browseLogosButton, javax.swing.GroupLayout.PREFERRED_SIZE, 67, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(agencyLogoPathFieldValidationLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(agencyLogoPreview, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) @@ -433,23 +592,25 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { logoPanelLayout.setVerticalGroup( logoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(logoPanelLayout.createSequentialGroup() - .addGroup(logoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(agencyLogoPreview, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(logoPanelLayout.createSequentialGroup() - .addContainerGap() - .addComponent(agencyLogoImageLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(logoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(agencyLogoPathField) - .addComponent(browseLogosButton)))) - .addGap(0, 0, 0)) + .addGap(6, 6, 6) + .addGroup(logoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(defaultLogoRB) + .addComponent(agencyLogoPathFieldValidationLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(logoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(specifyLogoRB) + .addComponent(agencyLogoPathField) + .addComponent(browseLogosButton))) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, logoPanelLayout.createSequentialGroup() + .addComponent(agencyLogoPreview, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE)) ); viewPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.viewPanel.border.title"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jLabelSelectFile, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelSelectFile.text")); // NOI18N - buttonGroup1.add(useBestViewerRB); + fileSelectionButtonGroup.add(useBestViewerRB); org.openide.awt.Mnemonics.setLocalizedText(useBestViewerRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.useBestViewerRB.text")); // NOI18N useBestViewerRB.setToolTipText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.useBestViewerRB.toolTipText")); // NOI18N useBestViewerRB.addActionListener(new java.awt.event.ActionListener() { @@ -458,7 +619,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { } }); - buttonGroup1.add(keepCurrentViewerRB); + fileSelectionButtonGroup.add(keepCurrentViewerRB); org.openide.awt.Mnemonics.setLocalizedText(keepCurrentViewerRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.keepCurrentViewerRB.text")); // NOI18N keepCurrentViewerRB.setToolTipText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.keepCurrentViewerRB.toolTipText")); // NOI18N keepCurrentViewerRB.addActionListener(new java.awt.event.ActionListener() { @@ -501,7 +662,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(jLabelTimeDisplay, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelTimeDisplay.text")); // NOI18N - buttonGroup3.add(useLocalTimeRB); + displayTimesButtonGroup.add(useLocalTimeRB); org.openide.awt.Mnemonics.setLocalizedText(useLocalTimeRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.useLocalTimeRB.text")); // NOI18N useLocalTimeRB.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -509,7 +670,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { } }); - buttonGroup3.add(useGMTTimeRB); + displayTimesButtonGroup.add(useGMTTimeRB); org.openide.awt.Mnemonics.setLocalizedText(useGMTTimeRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.useGMTTimeRB.text")); // NOI18N useGMTTimeRB.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -598,7 +759,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { } }); - invalidReasonLabel.setForeground(new java.awt.Color(255, 0, 0)); + memFieldValidationLabel.setForeground(new java.awt.Color(255, 0, 0)); org.openide.awt.Mnemonics.setLocalizedText(maxMemoryUnitsLabel1, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.maxMemoryUnitsLabel.text")); // NOI18N @@ -622,7 +783,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { .addGap(18, 18, 18) .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(restartNecessaryWarning, javax.swing.GroupLayout.DEFAULT_SIZE, 417, Short.MAX_VALUE) - .addComponent(invalidReasonLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(memFieldValidationLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); runtimePanelLayout.setVerticalGroup( @@ -633,7 +794,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(maxMemoryUnitsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(memField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(invalidReasonLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(memFieldValidationLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(maxMemoryLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) @@ -665,7 +826,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { .addComponent(runtimePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(logoPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, 0)) + .addContainerGap()) ); jScrollPane1.setViewportView(jPanel1); @@ -674,9 +835,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(jScrollPane1) - .addGap(0, 0, 0)) + .addComponent(jScrollPane1) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -718,9 +877,9 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { private void browseLogosButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseLogosButtonActionPerformed String oldLogoPath = agencyLogoPathField.getText(); - int returnState = fc.showOpenDialog(this); + int returnState = fileChooser.showOpenDialog(this); if (returnState == JFileChooser.APPROVE_OPTION) { - String path = fc.getSelectedFile().getPath(); + String path = fileChooser.getSelectedFile().getPath(); try { updateAgencyLogo(path); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); @@ -733,7 +892,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { try { updateAgencyLogo(oldLogoPath); //restore previous setting if new one is invalid } catch (IOException ex1) { - logger.log(Level.WARNING, "Error loading image from previously saved agency logo path", ex1); + LOGGER.log(Level.WARNING, "Error loading image from previously saved agency logo path", ex1); } } } @@ -748,53 +907,44 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); }//GEN-LAST:event_memFieldKeyReleased - /** - * Checks that if the mem field is enabled it has a valid value. - * - * @return true if the memfield is valid false if it is not - */ - private boolean isMemFieldValid() { - String memText = memField.getText(); - invalidReasonLabel.setText(""); - if (!PlatformUtil.is64BitJVM()) { - invalidReasonLabel.setText(Bundle.AutopsyOptionsPanel_invalidReasonLabel_not64BitInstall_text()); - //the panel should be valid when it is a 32 bit jvm because the memfield will be disabled. - return true; + private void defaultLogoRBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_defaultLogoRBActionPerformed + agencyLogoPathField.setEnabled(false); + browseLogosButton.setEnabled(false); + try { + updateAgencyLogo(""); + } catch (IOException ex) { + // This should never happen since we're not reading from a file. + LOGGER.log(Level.SEVERE, "Unexpected error occurred while updating the agency logo.", ex); } - if (Version.getBuildType() == Version.Type.DEVELOPMENT) { - invalidReasonLabel.setText(Bundle.AutopsyOptionsPanel_invalidReasonLabel_developerMode_text()); - //the panel should be valid when you are running in developer mode because the memfield will be disabled - return true; + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_defaultLogoRBActionPerformed + + private void specifyLogoRBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_specifyLogoRBActionPerformed + agencyLogoPathField.setEnabled(true); + browseLogosButton.setEnabled(true); + try { + if (agencyLogoPathField.getText().isEmpty()) { + String path = ModuleSettings.getConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP); + if (path != null && !path.isEmpty()) { + updateAgencyLogo(path); + } + } + } catch (IOException ex) { + LOGGER.log(Level.WARNING, "Error loading image from previously saved agency logo path.", ex); } - if (memText.length() == 0) { - invalidReasonLabel.setText(Bundle.AutopsyOptionsPanel_invalidReasonLabel_noValueEntered_text()); - return false; - } - if (memText.replaceAll("[^\\d]", "").length() != memText.length()) { - invalidReasonLabel.setText(Bundle.AutopsyOptionsPanel_invalidReasonLabel_invalidCharacters_text()); - return false; - } - int parsedInt = Integer.parseInt(memText); - if (parsedInt < MIN_MEMORY_IN_GB) { - invalidReasonLabel.setText(Bundle.AutopsyOptionsPanel_invalidReasonLabel_underMinMemory_text(MIN_MEMORY_IN_GB)); - return false; - } - if (parsedInt >= getSystemMemoryInGB()) { - invalidReasonLabel.setText(Bundle.AutopsyOptionsPanel_invalidReasonLabel_overMaxMemory_text(getSystemMemoryInGB())); - return false; - } - return true; - } + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + }//GEN-LAST:event_specifyLogoRBActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JLabel agencyLogoImageLabel; private javax.swing.JTextField agencyLogoPathField; + private javax.swing.JLabel agencyLogoPathFieldValidationLabel; private javax.swing.JLabel agencyLogoPreview; private javax.swing.JButton browseLogosButton; - private javax.swing.ButtonGroup buttonGroup1; - private javax.swing.ButtonGroup buttonGroup3; private javax.swing.JCheckBox dataSourcesHideKnownCB; private javax.swing.JCheckBox dataSourcesHideSlackCB; - private javax.swing.JLabel invalidReasonLabel; + private javax.swing.JRadioButton defaultLogoRB; + private javax.swing.ButtonGroup displayTimesButtonGroup; + private javax.swing.ButtonGroup fileSelectionButtonGroup; private javax.swing.JLabel jLabelHideKnownFiles; private javax.swing.JLabel jLabelHideSlackFiles; private javax.swing.JLabel jLabelSelectFile; @@ -803,12 +953,15 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { private javax.swing.JScrollPane jScrollPane1; private javax.swing.JRadioButton keepCurrentViewerRB; private javax.swing.JPanel logoPanel; + private javax.swing.ButtonGroup logoSourceButtonGroup; private javax.swing.JLabel maxMemoryLabel; private javax.swing.JLabel maxMemoryUnitsLabel; private javax.swing.JLabel maxMemoryUnitsLabel1; private javax.swing.JTextField memField; + private javax.swing.JLabel memFieldValidationLabel; private javax.swing.JLabel restartNecessaryWarning; private javax.swing.JPanel runtimePanel; + private javax.swing.JRadioButton specifyLogoRB; private javax.swing.JLabel systemMemoryTotal; private javax.swing.JLabel totalMemoryLabel; private javax.swing.JRadioButton useBestViewerRB; diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index 26a2cc03e0..6e9b722d71 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -192,8 +192,6 @@ AutopsyOptionsPanel.jLabelHideSlackFiles.text=Hide slack files in the: AutopsyOptionsPanel.dataSourcesHideSlackCB.text=Data Sources area (the directory hierarchy) AutopsyOptionsPanel.viewsHideSlackCB.text=Views area AutopsyOptionsPanel.agencyLogoImageLabel.toolTipText= -AutopsyOptionsPanel.agencyLogoImageLabel.text=Image to use as the agency logo for HTML reports: -AutopsyOptionsPanel.browseLogosButton.text=Browse AutopsyOptionsPanel.agencyLogoPathField.text= SortChooserDialog.label=remove SortChooser.addCriteriaButton.text=Add Sort Criteria @@ -201,3 +199,4 @@ DataResultViewerThumbnail.sortButton.text=Sort CriterionChooser.ascendingRadio.text=\u25b2 Ascending\n CriterionChooser.removeButton.text=Remove CriterionChooser.descendingRadio.text=\u25bc Descending +AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text= diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java index bcfa01f997..b415b190ea 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java @@ -29,6 +29,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Set; +import java.util.HashSet; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.AbstractAction; @@ -58,32 +60,33 @@ import org.sleuthkit.datamodel.VolumeSystem; * Extracts all the unallocated space as a single file */ final class ExtractUnallocAction extends AbstractAction { - - private final List LstUnallocs = new ArrayList(); - private static final List lockedVols = new ArrayList(); - private static final List lockedImages = new ArrayList(); - private long currentImage = 0L; private static final Logger logger = Logger.getLogger(ExtractUnallocAction.class.getName()); - private boolean isImage = false; - public ExtractUnallocAction(String title, Volume volu) { + private final List filesToExtract = new ArrayList<>(); + private static final Set volumesInProgress = new HashSet<>(); + private static final Set imagesInProgress = new HashSet<>(); + private long currentImage = 0L; + private final boolean isImage; + + public ExtractUnallocAction(String title, Volume volume) { super(title); - UnallocStruct us = new UnallocStruct(volu); - LstUnallocs.add(us); + isImage = false; + OutputFileData outputFileData = new OutputFileData(volume); + filesToExtract.add(outputFileData); } - public ExtractUnallocAction(String title, Image img) { + public ExtractUnallocAction(String title, Image image) { super(title); isImage = true; - currentImage = img.getId(); - if (hasVolumeSystem(img)) { - for (Volume v : getVolumes(img)) { - UnallocStruct us = new UnallocStruct(v); - LstUnallocs.add(us); + currentImage = image.getId(); + if (hasVolumeSystem(image)) { + for (Volume v : getVolumes(image)) { + OutputFileData outputFileData = new OutputFileData(v); + filesToExtract.add(outputFileData); } } else { - UnallocStruct us = new UnallocStruct(img); - LstUnallocs.add(us); + OutputFileData outputFileData = new OutputFileData(image); + filesToExtract.add(outputFileData); } } @@ -93,21 +96,28 @@ final class ExtractUnallocAction extends AbstractAction { * * @param e */ + @NbBundle.Messages({"# {0} - fileName", + "ExtractUnallocAction.volumeInProgress=Already extracting unallocated space into {0} - will skip this volume", + "ExtractUnallocAction.volumeError=Error extracting unallocated space from volume", + "ExtractUnallocAction.noFiles=No unallocated files found on volume", + "ExtractUnallocAction.imageError=Error extracting unallocated space from image"}) @Override public void actionPerformed(ActionEvent e) { - if (LstUnallocs != null && LstUnallocs.size() > 0) { - if (lockedImages.contains(currentImage)) { + if (filesToExtract != null && filesToExtract.size() > 0) { + // This check doesn't absolutely guarantee that the image won't be in progress when we make the worker, + // but in general it will suffice. + if (isImage && isImageInProgress(currentImage)) { MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.notifyMsg.unallocAlreadyBeingExtr.msg")); //JOptionPane.showMessageDialog(new Frame(), "Unallocated Space is already being extracted on this Image. Please select a different Image."); return; } - List copyList = new ArrayList() { + List copyList = new ArrayList() { { - addAll(LstUnallocs); + addAll(filesToExtract); } }; - JFileChooser fc = new JFileChooser() { + JFileChooser fileChooser = new JFileChooser() { @Override public void approveSelection() { File f = getSelectedFile(); @@ -120,45 +130,72 @@ final class ExtractUnallocAction extends AbstractAction { } }; - fc.setCurrentDirectory(new File(Case.getCurrentCase().getExportDirectory())); - fc.setDialogTitle( + fileChooser.setCurrentDirectory(new File(Case.getCurrentCase().getExportDirectory())); + fileChooser.setDialogTitle( NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg")); - fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - fc.setAcceptAllFileFilterUsed(false); - int returnValue = fc.showSaveDialog((Component) e.getSource()); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + fileChooser.setAcceptAllFileFilterUsed(false); + int returnValue = fileChooser.showSaveDialog((Component) e.getSource()); if (returnValue == JFileChooser.APPROVE_OPTION) { - String destination = fc.getSelectedFile().getPath(); - for (UnallocStruct u : LstUnallocs) { - u.setPath(destination); - if (u.llf != null && u.llf.size() > 0 && !lockedVols.contains(u.getFileName())) { - //Format for single Unalloc File is ImgName-Unalloc-ImgObjectID-VolumeID.dat - if (u.FileInstance.exists()) { + String destination = fileChooser.getSelectedFile().getPath(); + for (OutputFileData outputFileData : filesToExtract) { + outputFileData.setPath(destination); + + if (outputFileData.layoutFiles != null && outputFileData.layoutFiles.size() > 0 && (! isVolumeInProgress(outputFileData.getFileName()))) { + //Format for single Unalloc File is ImgName-Unalloc-ImgObjectID-VolumeID.dat + + // Check if there is already a file with this name + if (outputFileData.fileInstance.exists()) { int res = JOptionPane.showConfirmDialog(new Frame(), NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.confDlg.unallocFileAlreadyExist.msg", - u.getFileName())); + outputFileData.getFileName())); if (res == JOptionPane.YES_OPTION) { - u.FileInstance.delete(); + // If the user wants to overwrite, delete the exising output file + outputFileData.fileInstance.delete(); } else { - copyList.remove(u); + // Otherwise remove it from the list of output files + copyList.remove(outputFileData); } } + if (!isImage & !copyList.isEmpty()) { - ExtractUnallocWorker uw = new ExtractUnallocWorker(u); - uw.execute(); + try{ + ExtractUnallocWorker worker = new ExtractUnallocWorker(outputFileData); + worker.execute(); + } catch (Exception ex){ + logger.log(Level.WARNING, "Already extracting unallocated space into " + outputFileData.getFileName()); + MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeInProgress", outputFileData.getFileName())); + } } } else { - if(lockedVols.contains(u.getFileName())){ - logger.log(Level.WARNING, "Tried to get unallocated content but the volume is locked"); // NON_NLS - } else if (u.llf == null){ + // The output file for this volume could not be created for one of the following reasons + if (outputFileData.layoutFiles == null){ + MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeError")); logger.log(Level.SEVERE, "Tried to get unallocated content but the list of unallocated files was null"); //NON-NLS + } else if (outputFileData.layoutFiles.isEmpty()){ + MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.noFiles")); + logger.log(Level.WARNING, "No unallocated files found in volume"); //NON-NLS + copyList.remove(outputFileData); } else { - logger.log(Level.INFO, "No unallocated files found in volume"); //NON-NLS + MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeInProgress", outputFileData.getFileName())); + logger.log(Level.WARNING, "Tried to get unallocated content but the volume is locked"); // NON_NLS + copyList.remove(outputFileData); } } } + + // This needs refactoring. The idea seems to be that we'll take advantage of the loop above to + // check whether each output file exists but wait until this point to make a worker + // to extract everything (the worker in the above loop doesn't get created because isImage is true) + // It's also unclear to me why we need the two separate worker types. if (isImage && !copyList.isEmpty()) { - ExtractUnallocWorker uw = new ExtractUnallocWorker(copyList); - uw.execute(); + try{ + ExtractUnallocWorker worker = new ExtractUnallocWorker(copyList); + worker.execute(); + } catch (Exception ex){ + logger.log(Level.WARNING, "Error creating ExtractUnallocWorker", ex); + MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.imageError")); + } } } } @@ -185,7 +222,38 @@ final class ExtractUnallocAction extends AbstractAction { } return Collections.emptyList(); } - + + synchronized static private void addVolumeInProgress(String volumeOutputFileName) throws TskCoreException { + if(volumesInProgress.contains(volumeOutputFileName)){ + throw new TskCoreException("Already writing unallocated space to " + volumeOutputFileName); + } + volumesInProgress.add(volumeOutputFileName); + } + + synchronized static private void removeVolumeInProgress(String volumeOutputFileName){ + volumesInProgress.remove(volumeOutputFileName); + } + + synchronized static private boolean isVolumeInProgress(String volumeOutputFileName){ + return volumesInProgress.contains(volumeOutputFileName); + } + + synchronized static private void addImageInProgress(Long id) throws TskCoreException { + if(imagesInProgress.contains(id)){ + throw new TskCoreException("Image " + id + " is in use"); + } + imagesInProgress.add(id); + } + + synchronized static private void removeImageInProgress(Long id){ + imagesInProgress.remove(id); + } + + synchronized static private boolean isImageInProgress(Long id){ + return imagesInProgress.contains(id); + } + + /** * Private class for dispatching the file IO in a background thread. */ @@ -193,32 +261,40 @@ final class ExtractUnallocAction extends AbstractAction { private ProgressHandle progress; private boolean canceled = false; - private List lus = new ArrayList(); + private List outputFileDataList = new ArrayList<>(); private File currentlyProcessing; private int totalSizeinMegs; long totalBytes = 0; - ExtractUnallocWorker(UnallocStruct us) { + ExtractUnallocWorker(OutputFileData outputFileData) throws TskCoreException { //Getting the total megs this worker is going to be doing - if (!lockedVols.contains(us.getFileName())) { - this.lus.add(us); - totalBytes = us.getSizeInBytes(); - totalSizeinMegs = toMb(totalBytes); - lockedVols.add(us.getFileName()); - } + addVolumeInProgress(outputFileData.getFileName()); + outputFileDataList.add(outputFileData); + totalBytes = outputFileData.getSizeInBytes(); + totalSizeinMegs = toMb(totalBytes); } - ExtractUnallocWorker(List lst) { + ExtractUnallocWorker(List outputFileDataList) throws TskCoreException { + addImageInProgress(currentImage); + //Getting the total megs this worker is going to be doing - for (UnallocStruct lu : lst) { - if (!lockedVols.contains(lu.getFileName())) { - totalBytes += lu.getSizeInBytes(); - lockedVols.add(lu.getFileName()); - this.lus.add(lu); + for (OutputFileData outputFileData : outputFileDataList) { + try{ + // If a volume is locked, skip it but continue trying to process any other requested volumes + addVolumeInProgress(outputFileData.getFileName()); + totalBytes += outputFileData.getSizeInBytes(); + this.outputFileDataList.add(outputFileData); + } catch (TskCoreException ex){ + logger.log(Level.WARNING, "Already extracting data into " + outputFileData.getFileName()); } } + + // If we don't have anything to output (because of locking), throw an exception + if(this.outputFileDataList.isEmpty()){ + throw new TskCoreException("No unallocated files can be extracted"); + } + totalSizeinMegs = toMb(totalBytes); - lockedImages.add(currentImage); } private int toMb(long bytes) { @@ -254,47 +330,47 @@ final class ExtractUnallocAction extends AbstractAction { progress.start(totalSizeinMegs); int kbs = 0; //Each completion of the while loop adds one to kbs. 16kb * 64 = 1mb. int mbs = 0; //Increments every 128th tick of kbs - for (UnallocStruct u : this.lus) { - currentlyProcessing = u.getFile(); + for (OutputFileData outputFileData : this.outputFileDataList) { + currentlyProcessing = outputFileData.getFile(); logger.log(Level.INFO, "Writing Unalloc file to " + currentlyProcessing.getPath()); //NON-NLS - OutputStream dos = new FileOutputStream(currentlyProcessing); + OutputStream outputStream = new FileOutputStream(currentlyProcessing); long bytes = 0; int i = 0; - while (i < u.getLayouts().size() && bytes != u.getSizeInBytes()) { - LayoutFile f = u.getLayouts().get(i); + while (i < outputFileData.getLayouts().size() && bytes != outputFileData.getSizeInBytes()) { + LayoutFile layoutFile = outputFileData.getLayouts().get(i); long offsetPerFile = 0L; int bytesRead; - while (offsetPerFile != f.getSize() && !canceled) { + while (offsetPerFile != layoutFile.getSize() && !canceled) { if (++kbs % 128 == 0) { mbs++; progress.progress(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.processing.counter.msg", mbs, totalSizeinMegs), mbs - 1); } - bytesRead = f.read(buf, offsetPerFile, MAX_BYTES); + bytesRead = layoutFile.read(buf, offsetPerFile, MAX_BYTES); offsetPerFile += bytesRead; - dos.write(buf, 0, bytesRead); + outputStream.write(buf, 0, bytesRead); } - bytes += f.getSize(); + bytes += layoutFile.getSize(); i++; } - dos.flush(); - dos.close(); + outputStream.flush(); + outputStream.close(); if (canceled) { - u.getFile().delete(); - logger.log(Level.INFO, "Canceled extraction of " + u.getFileName() + " and deleted file"); //NON-NLS + outputFileData.getFile().delete(); + logger.log(Level.INFO, "Canceled extraction of " + outputFileData.getFileName() + " and deleted file"); //NON-NLS } else { - logger.log(Level.INFO, "Finished writing unalloc file " + u.getFile().getPath()); //NON-NLS + logger.log(Level.INFO, "Finished writing unalloc file " + outputFileData.getFile().getPath()); //NON-NLS } } progress.finish(); - } catch (IOException ioe) { - logger.log(Level.WARNING, "Could not create Unalloc File; error writing file", ioe); //NON-NLS + } catch (IOException ex) { + logger.log(Level.WARNING, "Could not create Unalloc File; error writing file", ex); //NON-NLS return -1; - } catch (TskCoreException tce) { - logger.log(Level.WARNING, "Could not create Unalloc File; error getting image info", tce); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Could not create Unalloc File; error getting image info", ex); //NON-NLS return -1; } return 1; @@ -303,20 +379,20 @@ final class ExtractUnallocAction extends AbstractAction { @Override protected void done() { if (isImage) { - lockedImages.remove(currentImage); + removeImageInProgress(currentImage); } - for (UnallocStruct u : lus) { - lockedVols.remove(u.getFileName()); + for (OutputFileData u : outputFileDataList) { + removeVolumeInProgress(u.getFileName()); } try { get(); - if (!canceled && !lus.isEmpty()) { + if (!canceled && !outputFileDataList.isEmpty()) { MessageNotifyUtil.Notify.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.done.notifyMsg.completedExtract.title"), NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.done.notifyMsg.completedExtract.msg", - lus.get(0).getFile().getParent())); + outputFileDataList.get(0).getFile().getParent())); } } catch (InterruptedException | ExecutionException ex) { MessageNotifyUtil.Notify.error( @@ -342,8 +418,8 @@ final class ExtractUnallocAction extends AbstractAction { return true; } } - } catch (TskCoreException tce) { - logger.log(Level.SEVERE, "Unable to determine if image has a volume system, extraction may be incomplete", tce); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Unable to determine if image has a volume system, extraction may be incomplete", ex); //NON-NLS } return false; } @@ -357,17 +433,17 @@ final class ExtractUnallocAction extends AbstractAction { * matches. */ private List getVolumes(Image img) { - List lstVol = new ArrayList(); + List volumes = new ArrayList<>(); try { for (Content v : img.getChildren().get(0).getChildren()) { if (v instanceof Volume) { - lstVol.add((Volume) v); + volumes.add((Volume) v); } } } catch (TskCoreException tce) { logger.log(Level.WARNING, "Could not get volume information from image. Extraction may be incomplete", tce); //NON-NLS } - return lstVol; + return volumes; } /** @@ -412,8 +488,8 @@ final class ExtractUnallocAction extends AbstractAction { } } } - } catch (TskCoreException tce) { - logger.log(Level.WARNING, "Couldn't get a list of Unallocated Files, failed at visiting FileSystem " + fs.getId(), tce); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Couldn't get a list of Unallocated Files, failed at visiting FileSystem " + fs.getId(), ex); //NON-NLS } return Collections.emptyList(); } @@ -429,15 +505,15 @@ final class ExtractUnallocAction extends AbstractAction { @Override public List visit(VirtualDirectory vd) { try { - List lflst = new ArrayList<>(); + List layoutFiles = new ArrayList<>(); for (Content layout : vd.getChildren()) { if (layout instanceof LayoutFile) { - lflst.add((LayoutFile) layout); + layoutFiles.add((LayoutFile) layout); } } - return lflst; - } catch (TskCoreException tce) { - logger.log(Level.WARNING, "Could not get list of Layout Files, failed at visiting Layout Directory", tce); //NON-NLS + return layoutFiles; + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Could not get list of Layout Files, failed at visiting Layout Directory", ex); //NON-NLS } return Collections.emptyList(); } @@ -455,12 +531,13 @@ final class ExtractUnallocAction extends AbstractAction { public List visit(Directory dir) { try { for (Content c : dir.getChildren()) { + // Only the $Unalloc dir will contain unallocated files if ((c instanceof VirtualDirectory) && (c.getName().equals(VirtualDirectory.NAME_UNALLOC))) { return c.accept(this); } } - } catch (TskCoreException tce) { - logger.log(Level.WARNING, "Couldn't get a list of Unallocated Files, failed at visiting Directory " + dir.getId(), tce); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Couldn't get a list of Unallocated Files, failed at visiting Directory " + dir.getId(), ex); //NON-NLS } return Collections.emptyList(); } @@ -495,97 +572,97 @@ final class ExtractUnallocAction extends AbstractAction { * Private class for assisting in the running the action over an image with * multiple volumes. */ - private class UnallocStruct { + private class OutputFileData { - private List llf; - private long SizeInBytes; - private long VolumeId; - private long ImageId; - private String ImageName; - private String FileName; - private File FileInstance; + private List layoutFiles; + private final long sizeInBytes; + private long volumeId; + private long imageId; + private String imageName; + private final String fileName; + private File fileInstance; /** * Contingency constructor in event no VolumeSystem exists on an Image. * * @param img Image file to be analyzed */ - UnallocStruct(Image img) { - this.llf = getUnallocFiles(img); - Collections.sort(llf, new SortObjId()); - this.VolumeId = 0; - this.ImageId = img.getId(); - this.ImageName = img.getName(); - this.FileName = this.ImageName + "-Unalloc-" + this.ImageId + "-" + 0 + ".dat"; //NON-NLS - this.FileInstance = new File(Case.getCurrentCase().getExportDirectory() + File.separator + this.FileName); - this.SizeInBytes = calcSizeInBytes(); + OutputFileData(Image img) { + this.layoutFiles = getUnallocFiles(img); + Collections.sort(layoutFiles, new SortObjId()); + this.volumeId = 0; + this.imageId = img.getId(); + this.imageName = img.getName(); + this.fileName = this.imageName + "-Unalloc-" + this.imageId + "-" + 0 + ".dat"; //NON-NLS + this.fileInstance = new File(Case.getCurrentCase().getExportDirectory() + File.separator + this.fileName); + this.sizeInBytes = calcSizeInBytes(); } /** * Default constructor for extracting info from Volumes. * - * @param volu Volume file to be analyzed + * @param volume Volume file to be analyzed */ - UnallocStruct(Volume volu) { + OutputFileData(Volume volume) { try { - this.ImageName = volu.getDataSource().getName(); - this.ImageId = volu.getDataSource().getId(); - this.VolumeId = volu.getId(); + this.imageName = volume.getDataSource().getName(); + this.imageId = volume.getDataSource().getId(); + this.volumeId = volume.getId(); } catch (TskCoreException tce) { logger.log(Level.WARNING, "Unable to properly create ExtractUnallocAction, extraction may be incomplete", tce); //NON-NLS - this.ImageName = ""; - this.ImageId = 0; + this.imageName = ""; + this.imageId = 0; } - this.FileName = this.ImageName + "-Unalloc-" + this.ImageId + "-" + VolumeId + ".dat"; //NON-NLS - this.FileInstance = new File(Case.getCurrentCase().getExportDirectory() + File.separator + this.FileName); - this.llf = getUnallocFiles(volu); - Collections.sort(llf, new SortObjId()); - this.SizeInBytes = calcSizeInBytes(); + this.fileName = this.imageName + "-Unalloc-" + this.imageId + "-" + volumeId + ".dat"; //NON-NLS + this.fileInstance = new File(Case.getCurrentCase().getExportDirectory() + File.separator + this.fileName); + this.layoutFiles = getUnallocFiles(volume); + Collections.sort(layoutFiles, new SortObjId()); + this.sizeInBytes = calcSizeInBytes(); } //Getters int size() { - return llf.size(); + return layoutFiles.size(); } private long calcSizeInBytes() { long size = 0L; - for (LayoutFile f : llf) { + for (LayoutFile f : layoutFiles) { size += f.getSize(); } return size; } long getSizeInBytes() { - return this.SizeInBytes; + return this.sizeInBytes; } long getVolumeId() { - return this.VolumeId; + return this.volumeId; } long getImageId() { - return this.ImageId; + return this.imageId; } String getImageName() { - return this.ImageName; + return this.imageName; } List getLayouts() { - return this.llf; + return this.layoutFiles; } String getFileName() { - return this.FileName; + return this.fileName; } File getFile() { - return this.FileInstance; + return this.fileInstance; } void setPath(String path) { - this.FileInstance = new File(path + File.separator + this.FileName); + this.fileInstance = new File(path + File.separator + this.fileName); } } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java index 3ffaa01622..396e95dc0d 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java @@ -61,7 +61,7 @@ final class SearchRunner { private static SearchRunner instance = null; private IngestServices services = IngestServices.getInstance(); private Ingester ingester = null; - private long defaultUpdateIntervalMs; + private long currentUpdateIntervalMs; private volatile boolean periodicSearchTaskRunning = false; private Future jobProcessingTaskFuture; private final ScheduledThreadPoolExecutor jobProcessingExecutor; @@ -72,7 +72,7 @@ final class SearchRunner { private Map jobs = new ConcurrentHashMap<>(); SearchRunner() { - defaultUpdateIntervalMs = ((long) KeywordSearchSettings.getUpdateFrequency().getTime()) * 60 * 1000; + currentUpdateIntervalMs = ((long) KeywordSearchSettings.getUpdateFrequency().getTime()) * 60 * 1000; ingester = Ingester.getDefault(); jobProcessingExecutor = new ScheduledThreadPoolExecutor(NUM_SEARCH_SCHEDULING_THREADS, new ThreadFactoryBuilder().setNameFormat(SEARCH_SCHEDULER_THREAD_NAME).build()); } @@ -108,8 +108,8 @@ final class SearchRunner { if ((jobs.size() > 0) && (periodicSearchTaskRunning == false)) { // reset the default periodic search frequency to the user setting logger.log(Level.INFO, "Resetting periodic search time out to default value"); //NON-NLS - defaultUpdateIntervalMs = ((long) KeywordSearchSettings.getUpdateFrequency().getTime()) * 60 * 1000; - jobProcessingTaskFuture = jobProcessingExecutor.schedule(new PeriodicSearchTask(), defaultUpdateIntervalMs, MILLISECONDS); + currentUpdateIntervalMs = ((long) KeywordSearchSettings.getUpdateFrequency().getTime()) * 60 * 1000; + jobProcessingTaskFuture = jobProcessingExecutor.schedule(new PeriodicSearchTask(), currentUpdateIntervalMs, MILLISECONDS); periodicSearchTaskRunning = true; } } @@ -304,25 +304,25 @@ final class SearchRunner { logger.log(Level.INFO, "All periodic searches cumulatively took {0} secs", stopWatch.getElapsedTimeSecs()); //NON-NLS // calculate "hold off" time - final long timeToTextSearchMs = getTimeToNextPeriodicSearch(stopWatch.getElapsedTimeSecs()); // ELDEBUG + recalculateUpdateIntervalTime(stopWatch.getElapsedTimeSecs()); // ELDEBUG // schedule next PeriodicSearchTask - jobProcessingTaskFuture = jobProcessingExecutor.schedule(new PeriodicSearchTask(), timeToTextSearchMs, MILLISECONDS); + jobProcessingTaskFuture = jobProcessingExecutor.schedule(new PeriodicSearchTask(), currentUpdateIntervalMs, MILLISECONDS); // exit this thread return; } - private long getTimeToNextPeriodicSearch(long lastSerchTimeSec) { + private void recalculateUpdateIntervalTime(long lastSerchTimeSec) { // If periodic search takes more than 1/4 of the current periodic search interval, then double the search interval - if (lastSerchTimeSec * 1000 < defaultUpdateIntervalMs / 4) { - return defaultUpdateIntervalMs; + if (lastSerchTimeSec * 1000 < currentUpdateIntervalMs / 4) { + return; } // double the search interval - defaultUpdateIntervalMs = defaultUpdateIntervalMs * 2; - logger.log(Level.WARNING, "Last periodic search took {0} sec. Increasing search interval to {1} sec", new Object[]{lastSerchTimeSec, defaultUpdateIntervalMs/1000}); - return defaultUpdateIntervalMs; + currentUpdateIntervalMs = currentUpdateIntervalMs * 2; + logger.log(Level.WARNING, "Last periodic search took {0} sec. Increasing search interval to {1} sec", new Object[]{lastSerchTimeSec, currentUpdateIntervalMs/1000}); + return; } } diff --git a/docs/doxygen-user/adHocKeywordSearch.dox b/docs/doxygen-user/adHocKeywordSearch.dox index 606ce65911..ec7c1ab689 100755 --- a/docs/doxygen-user/adHocKeywordSearch.dox +++ b/docs/doxygen-user/adHocKeywordSearch.dox @@ -37,9 +37,9 @@ Substring match should be used where the search term is just part of a word, or ## Regex match -Regex match can be used to search for a specific pattern. Regular expressions are supported using Lucene Regex Syntax which is documented here: https://lucene.apache.org/core/6_4_0/core/org/apache/lucene/util/automaton/RegExp.html. .* is automatically added to the beginning and end of the regular expressions to ensure all matches are found. Additionally, the resulting hits are split on common token separator boundaries (e.g. space, newline, colon, exclamation point etc.) to make the resulting keyword hit more amenable to highlighting. +Regex match can be used to search for a specific pattern. Regular expressions are supported using Lucene Regex Syntax which is documented here: https://www.elastic.co/guide/en/elasticsearch/reference/1.6/query-dsl-regexp-query.html#regexp-syntax. Wildcards are automatically added to the beginning and end of the regular expressions to ensure all matches are found. Additionally, the resulting hits are split on common token separator boundaries (e.g. space, newline, colon, exclamation point etc.) to make the resulting keyword hit more amenable to highlighting. -There is some validation on the regex but it's best to test on a sample image to make sure your regexes are correct and working as expected. +There is some validation on the regex but it's best to test on a sample image to make sure your regexes are correct and working as expected. One simple way to test is by creating a sample text file that your expression should match, ingesting it as a \ref ds_log "Logical File Set" and then running the regex query. > In the year 1885 in an article titled Current Notes, the quick brown fox first jumped over the lazy dog. diff --git a/nbproject/project.properties b/nbproject/project.properties index 04a6d5800b..d97ffbaecf 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 diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java index 91c4c2ff5c..10dc2d31ce 100755 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java @@ -368,7 +368,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { * * @param input - input string, like the To/CC line from an email header * - * @param Set: set of email addresses found in the input string + * @return Set: set of email addresses found in the input string */ private Set findEmailAddresess(String input) { Pattern p = Pattern.compile("\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b",