diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.form b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.form deleted file mode 100644 index 928cb45a95..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.form +++ /dev/null @@ -1,132 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java deleted file mode 100644 index adf61a8f32..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011-2014 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.BorderLayout; -import java.awt.Component; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import javax.swing.JList; -import javax.swing.JPanel; -import javax.swing.JSeparator; -import javax.swing.ListCellRenderer; -import javax.swing.event.DocumentEvent; -import org.openide.util.Lookup; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datasourceprocessors.RawDSProcessor; - -/** - * visual component for the first panel of add image wizard. Allows the user to - * choose the data source type and then select the data source - * - */ -final class AddImageWizardChooseDataSourceVisual extends JPanel { - - static final Logger logger = Logger.getLogger(AddImageWizardChooseDataSourceVisual.class.getName()); - - private AddImageWizardChooseDataSourcePanel wizPanel; - - private JPanel currentPanel; - - private Map datasourceProcessorsMap = new HashMap<>(); - - List coreDSPTypes = new ArrayList<>(); - - /** - * Creates new form AddImageVisualPanel1 - * - * @param wizPanel corresponding WizardPanel to handle logic of wizard step - */ - AddImageWizardChooseDataSourceVisual(AddImageWizardChooseDataSourcePanel wizPanel) { - initComponents(); - this.wizPanel = wizPanel; - - customInit(); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private void customInit() { - - typePanel.setLayout(new BorderLayout()); - - discoverDataSourceProcessors(); - - // set up the DSP type combobox - typeComboBox.removeAllItems(); - - Set dspTypes = datasourceProcessorsMap.keySet(); - - // make a list of core DSPs - // ensure that the core DSPs are at the top and in a fixed order - coreDSPTypes.add(ImageDSProcessor.getType()); - // Local disk processing is not allowed for multi-user cases - if (Case.getCurrentCase().getCaseType() != Case.CaseType.MULTI_USER_CASE) { - coreDSPTypes.add(LocalDiskDSProcessor.getType()); - } else { - // remove LocalDiskDSProcessor from list of DSPs - datasourceProcessorsMap.remove(LocalDiskDSProcessor.getType()); - } - coreDSPTypes.add(LocalFilesDSProcessor.getType()); - coreDSPTypes.add(RawDSProcessor.getType()); - - for (String dspType : coreDSPTypes) { - typeComboBox.addItem(dspType); - } - - // now add any addtional DSPs that haven't already been added - for (String dspType : dspTypes) { - if (!coreDSPTypes.contains(dspType)) { - typeComboBox.addItem(dspType); - } - } - - typeComboBox.setRenderer(new ComboboxSeparatorRenderer(typeComboBox.getRenderer()) { - - @Override - protected boolean addSeparatorAfter(JList list, Object value, int index) { - return (index == coreDSPTypes.size() - 1); - } - }); - - //add actionlistner to listen for change - ActionListener cbActionListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - dspSelectionChanged(); - } - }; - typeComboBox.addActionListener(cbActionListener); - typeComboBox.setSelectedIndex(0); - } - - private void discoverDataSourceProcessors() { - - for (DataSourceProcessor dsProcessor : Lookup.getDefault().lookupAll(DataSourceProcessor.class)) { - - if (!datasourceProcessorsMap.containsKey(dsProcessor.getDataSourceType())) { - datasourceProcessorsMap.put(dsProcessor.getDataSourceType(), dsProcessor); - } else { - logger.log(Level.SEVERE, "discoverDataSourceProcessors(): A DataSourceProcessor already exists for type = {0}", dsProcessor.getDataSourceType()); //NON-NLS - } - } - } - - private void dspSelectionChanged() { - // update the current panel to selection - currentPanel = getCurrentDSProcessor().getPanel(); - updateCurrentPanel(currentPanel); - } - - /** - * Changes the current panel to the given panel. - * - * @param panel instance of ImageTypePanel to change to - */ - @SuppressWarnings("deprecation") - private void updateCurrentPanel(JPanel panel) { - currentPanel = panel; - typePanel.removeAll(); - typePanel.add(currentPanel, BorderLayout.CENTER); - typePanel.validate(); - typePanel.repaint(); - currentPanel.addPropertyChangeListener(new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString())) { - updateUI(null); - } - if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString())) { - wizPanel.moveFocusToNext(); - } - } - }); - - updateUI(null); - } - - /** - * Returns the currently selected DS Processor - * - * @return DataSourceProcessor the DataSourceProcessor corresponding to the - * data source type selected in the combobox - */ - protected DataSourceProcessor getCurrentDSProcessor() { - // get the type of the currently selected panel and then look up - // the correspodning DS Handler in the map - String dsType = (String) typeComboBox.getSelectedItem(); - DataSourceProcessor dsProcessor = datasourceProcessorsMap.get(dsType); - - return dsProcessor; - - } - - /** - * Returns the name of the this panel. This name will be shown on the left - * panel of the "Add Image" wizard panel. - * - * @return name the name of this panel - */ - @Override - public String getName() { - return NbBundle.getMessage(this.getClass(), "AddImageWizardChooseDataSourceVisual.getName.text"); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - // //GEN-BEGIN:initComponents - private void initComponents() { - - buttonGroup1 = new javax.swing.ButtonGroup(); - jLabel2 = new javax.swing.JLabel(); - inputPanel = new javax.swing.JPanel(); - typeTabel = new javax.swing.JLabel(); - typePanel = new javax.swing.JPanel(); - typeComboBox = new javax.swing.JComboBox(); - - org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(AddImageWizardChooseDataSourceVisual.class, "AddImageWizardChooseDataSourceVisual.jLabel2.text")); // NOI18N - - setPreferredSize(new java.awt.Dimension(588, 328)); - - org.openide.awt.Mnemonics.setLocalizedText(typeTabel, org.openide.util.NbBundle.getMessage(AddImageWizardChooseDataSourceVisual.class, "AddImageWizardChooseDataSourceVisual.typeTabel.text")); // NOI18N - - typePanel.setMinimumSize(new java.awt.Dimension(0, 65)); - typePanel.setPreferredSize(new java.awt.Dimension(521, 65)); - - javax.swing.GroupLayout typePanelLayout = new javax.swing.GroupLayout(typePanel); - typePanel.setLayout(typePanelLayout); - typePanelLayout.setHorizontalGroup( - typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 548, Short.MAX_VALUE) - ); - typePanelLayout.setVerticalGroup( - typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 225, Short.MAX_VALUE) - ); - - javax.swing.GroupLayout inputPanelLayout = new javax.swing.GroupLayout(inputPanel); - inputPanel.setLayout(inputPanelLayout); - inputPanelLayout.setHorizontalGroup( - inputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(inputPanelLayout.createSequentialGroup() - .addContainerGap() - .addGroup(inputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(inputPanelLayout.createSequentialGroup() - .addComponent(typeTabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(typeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 292, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 119, Short.MAX_VALUE)) - .addComponent(typePanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 548, Short.MAX_VALUE)) - .addContainerGap()) - ); - inputPanelLayout.setVerticalGroup( - inputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(inputPanelLayout.createSequentialGroup() - .addContainerGap() - .addGroup(inputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(typeTabel) - .addComponent(typeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(typePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 225, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - - 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(inputPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addContainerGap()) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(6, 6, 6) - .addComponent(inputPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(44, Short.MAX_VALUE)) - ); - }// //GEN-END:initComponents - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.ButtonGroup buttonGroup1; - private javax.swing.JPanel inputPanel; - private javax.swing.JLabel jLabel2; - private javax.swing.JComboBox typeComboBox; - private javax.swing.JPanel typePanel; - private javax.swing.JLabel typeTabel; - // End of variables declaration//GEN-END:variables - - /** - * The "listener" that updates the UI of this panel based on the changes of - * fields on this panel. This is also the method to check whether all the - * fields on this panel are correctly filled and decides whether to enable - * the "Next" button or not. - * - * @param e the document event - */ - public void updateUI(DocumentEvent e) { - // Enable the Next button if the current DSP panel is valid - this.wizPanel.enableNextButton(getCurrentDSProcessor().isPanelValid()); - } - - @SuppressWarnings("rawtypes") - public abstract class ComboboxSeparatorRenderer implements ListCellRenderer { - - private ListCellRenderer delegate; - - private JPanel separatorPanel = new JPanel(new BorderLayout()); - - private JSeparator separator = new JSeparator(); - - public ComboboxSeparatorRenderer(ListCellRenderer delegate) { - this.delegate = delegate; - } - - @SuppressWarnings("unchecked") - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - Component comp = delegate.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (index != -1 && addSeparatorAfter(list, value, index)) { - separatorPanel.removeAll(); - separatorPanel.add(comp, BorderLayout.CENTER); - separatorPanel.add(separator, BorderLayout.SOUTH); - return separatorPanel; - } else { - return comp; - } - } - - protected abstract boolean addSeparatorAfter(JList list, Object value, int index); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourcePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsPanel.java similarity index 81% rename from Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourcePanel.java rename to Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsPanel.java index 3d58017ecb..1643769be0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourcePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsPanel.java @@ -39,23 +39,17 @@ import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescript * The "Add Image" wizard panel1 handling the logic of selecting image file(s) * to add to Case, and pick the time zone. */ -class AddImageWizardChooseDataSourcePanel extends ShortcutWizardDescriptorPanel implements PropertyChangeListener { +class AddImageWizardDataSourceSettingsPanel extends ShortcutWizardDescriptorPanel implements PropertyChangeListener { /** * The visual component that displays this panel. If you need to access the * component from this class, just use getComponent(). */ - private final AddImageWizardAddingProgressPanel progressPanel; - private AddImageWizardChooseDataSourceVisual component; + private AddImageWizardDataSourceSettingsVisual component; private boolean isNextEnable = false; - private static final String PROP_LASTDATASOURCE_PATH = "LBL_LastDataSource_PATH"; //NON-NLS - private static final String PROP_LASTDATASOURCE_TYPE = "LBL_LastDataSource_TYPE"; //NON-NLS // paths to any set hash lookup databases (can be null) - private String NSRLPath, knownBadPath; - AddImageWizardChooseDataSourcePanel(AddImageWizardAddingProgressPanel proPanel) { - - this.progressPanel = proPanel; + AddImageWizardDataSourceSettingsPanel() { } @@ -68,10 +62,10 @@ class AddImageWizardChooseDataSourcePanel extends ShortcutWizardDescriptorPanel * @return component the UI component of this wizard panel */ @Override - public AddImageWizardChooseDataSourceVisual getComponent() { + public AddImageWizardDataSourceSettingsVisual getComponent() { if (component == null) { WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - component = new AddImageWizardChooseDataSourceVisual(this); + component = new AddImageWizardDataSourceSettingsVisual(this); } component.addPropertyChangeListener(this); return component; @@ -87,8 +81,7 @@ class AddImageWizardChooseDataSourcePanel extends ShortcutWizardDescriptorPanel public HelpCtx getHelp() { // Show no Help button for this panel: return HelpCtx.DEFAULT_HELP; - // If you have context help: - // return new HelpCtx(SampleWizardPanel1.class); + } /** @@ -164,10 +157,6 @@ class AddImageWizardChooseDataSourcePanel extends ShortcutWizardDescriptorPanel } } - // You can use a settings object to keep track of state. Normally the - // settings object will be the WizardDescriptor, so you can use - // WizardDescriptor.getProperty & putProperty to store information entered - // by the user. /** * Provides the wizard panel with the current data--either the default data * or already-modified settings, if the user used the previous and/or next @@ -178,26 +167,16 @@ class AddImageWizardChooseDataSourcePanel extends ShortcutWizardDescriptorPanel */ @Override public void readSettings(WizardDescriptor settings) { - - //reset settings if supports it - //getComponent().reset(); // Prepopulate the image directory from the properties file try { - - // Load hash database settings, enable or disable the checkbox - this.NSRLPath = null; - this.knownBadPath = null; - //JCheckBox lookupFilesCheckbox = component.getLookupFilesCheckbox(); - //lookupFilesCheckbox.setSelected(false); - //lookupFilesCheckbox.setEnabled(this.NSRLPath != null || this.knownBadPath != null); - + // If there is a process object in the settings, revert it and remove it from the settings AddImageAction.CleanupTask cleanupTask = (AddImageAction.CleanupTask) settings.getProperty(AddImageAction.IMAGECLEANUPTASK_PROP); if (cleanupTask != null) { try { cleanupTask.cleanup(); } catch (Exception ex) { - Logger logger = Logger.getLogger(AddImageWizardChooseDataSourcePanel.class.getName()); + Logger logger = Logger.getLogger(AddImageWizardDataSourceSettingsPanel.class.getName()); logger.log(Level.WARNING, "Error cleaning up image task", ex); //NON-NLS } finally { cleanupTask.disable(); @@ -205,7 +184,7 @@ class AddImageWizardChooseDataSourcePanel extends ShortcutWizardDescriptorPanel } } catch (Exception e) { } - + component.setDspSelection((String)settings.getProperty("SelectedDsp")); //NON-NLS magic string used SelectDataSourceProcessorPanel } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.form b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.form new file mode 100644 index 0000000000..d8ab744f34 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.form @@ -0,0 +1,58 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.java new file mode 100644 index 0000000000..a5671ce0ee --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.java @@ -0,0 +1,215 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.casemodule; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.ListCellRenderer; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * visual component for the first panel of add image wizard. Allows the user to + * choose the data source type and then select the data source + * + */ +final class AddImageWizardDataSourceSettingsVisual extends JPanel { + + private static final Logger logger = Logger.getLogger(AddImageWizardDataSourceSettingsVisual.class.getName()); + + private final AddImageWizardDataSourceSettingsPanel wizPanel; + + private JPanel currentPanel; + + private final Map datasourceProcessorsMap = new HashMap<>(); + + private String currentDsp; + + /** + * Creates new form AddImageVisualPanel1 + * + * @param wizPanel corresponding WizardPanel to handle logic of wizard step + */ + AddImageWizardDataSourceSettingsVisual(AddImageWizardDataSourceSettingsPanel wizPanel) { + initComponents(); + this.wizPanel = wizPanel; + typePanel.setLayout(new BorderLayout()); + discoverDataSourceProcessors(); + currentDsp = ImageDSProcessor.getType(); //default value to the ImageDSProcessor + } + + /** + * Populate the map of DataSourceProcessors which so they can be retrieved + * by name. + */ + private void discoverDataSourceProcessors() { + for (DataSourceProcessor dsProcessor : Lookup.getDefault().lookupAll(DataSourceProcessor.class)) { + if (!datasourceProcessorsMap.containsKey(dsProcessor.getDataSourceType())) { + datasourceProcessorsMap.put(dsProcessor.getDataSourceType(), dsProcessor); + } else { + logger.log(Level.SEVERE, "discoverDataSourceProcessors(): A DataSourceProcessor already exists for type = {0}", dsProcessor.getDataSourceType()); //NON-NLS + } + } + } + + /** + * Set the current DataSourceProcessor and update the panel to reflect that + * selection. + * + * @param dsType - the name of the DataSourceProcessor you wish to have this + * panel display settings for. + */ + void setDspSelection(String dsType) { + currentDsp = dsType; + currentPanel = datasourceProcessorsMap.get(dsType).getPanel(); + updateCurrentPanel(currentPanel); + } + + /** + * Changes the current panel to the given panel. + * + * @param panel instance of ImageTypePanel to change to + */ + @SuppressWarnings("deprecation") + private void updateCurrentPanel(JPanel panel) { + currentPanel = panel; + typePanel.removeAll(); + typePanel.add(currentPanel, BorderLayout.CENTER); + typePanel.validate(); + typePanel.repaint(); + currentPanel.addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString())) { + wizPanel.enableNextButton(getCurrentDSProcessor().isPanelValid()); + } + if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString())) { + wizPanel.moveFocusToNext(); + } + } + }); + this.wizPanel.enableNextButton(getCurrentDSProcessor().isPanelValid()); + } + + /** + * Returns the currently selected DS Processor + * + * @return DataSourceProcessor the DataSourceProcessor corresponding to the + * data source type selected in the combobox + */ + protected DataSourceProcessor getCurrentDSProcessor() { + // get the type of the currently selected panel and then look up + // the correspodning DS Handler in the map + DataSourceProcessor dsProcessor = datasourceProcessorsMap.get(currentDsp); + return dsProcessor; + + } + + /** + * Returns the name of the this panel. This name will be shown on the left + * panel of the "Add Image" wizard panel. + * + * @return name the name of this panel + */ + @Override + public String getName() { + return NbBundle.getMessage(this.getClass(), "AddImageWizardChooseDataSourceVisual.getName.text"); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + + typePanel = new javax.swing.JPanel(); + + setPreferredSize(new java.awt.Dimension(588, 328)); + + typePanel.setMinimumSize(new java.awt.Dimension(0, 65)); + typePanel.setPreferredSize(new java.awt.Dimension(521, 65)); + + javax.swing.GroupLayout typePanelLayout = new javax.swing.GroupLayout(typePanel); + typePanel.setLayout(typePanelLayout); + typePanelLayout.setHorizontalGroup( + typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 588, Short.MAX_VALUE) + ); + typePanelLayout.setVerticalGroup( + typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 328, Short.MAX_VALUE) + ); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(typePanel, javax.swing.GroupLayout.DEFAULT_SIZE, 588, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(typePanel, javax.swing.GroupLayout.DEFAULT_SIZE, 328, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JPanel typePanel; + // End of variables declaration//GEN-END:variables + + @SuppressWarnings("rawtypes") + public abstract class ComboboxSeparatorRenderer implements ListCellRenderer { + + private final ListCellRenderer delegate; + + private final JPanel separatorPanel = new JPanel(new BorderLayout()); + + private final JSeparator separator = new JSeparator(); + + public ComboboxSeparatorRenderer(ListCellRenderer delegate) { + this.delegate = delegate; + } + + @SuppressWarnings("unchecked") + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + Component comp = delegate.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (index != -1 && addSeparatorAfter(list, value, index)) { + separatorPanel.removeAll(); + separatorPanel.add(comp, BorderLayout.CENTER); + separatorPanel.add(separator, BorderLayout.SOUTH); + return separatorPanel; + } else { + return comp; + } + } + + protected abstract boolean addSeparatorAfter(JList list, Object value, int index); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java index ce4aa13c89..1d7bbb8a13 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java @@ -69,12 +69,12 @@ class AddImageWizardIngestConfigPanel extends ShortcutWizardDescriptorPanel { private final AddImageAction addImageAction; private final AddImageWizardAddingProgressPanel progressPanel; - private final AddImageWizardChooseDataSourcePanel dataSourcePanel; + private final AddImageWizardDataSourceSettingsPanel dataSourcePanel; private DataSourceProcessor dsProcessor; private boolean cancelled; - AddImageWizardIngestConfigPanel(AddImageWizardChooseDataSourcePanel dsPanel, AddImageAction action, AddImageWizardAddingProgressPanel proPanel) { + AddImageWizardIngestConfigPanel(AddImageWizardDataSourceSettingsPanel dsPanel, AddImageAction action, AddImageWizardAddingProgressPanel proPanel) { this.addImageAction = action; this.progressPanel = proPanel; this.dataSourcePanel = dsPanel; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java index 3dafa75479..3558ffe83b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIterator.java @@ -40,6 +40,7 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator panels; private final AddImageAction action; private int progressPanelIndex; + private int dsPanelIndex; private final static String PROP_LASTPROFILE_NAME = "AIW_LASTPROFILE_NAME"; //NON-NLS AddImageWizardIterator(AddImageAction action) { @@ -53,10 +54,11 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator getPanels() { if (panels == null) { panels = new ArrayList<>(); - + AddImageWizardSelectDspPanel dspSelection = new AddImageWizardSelectDspPanel(); + panels.add(dspSelection); AddImageWizardAddingProgressPanel progressPanel = new AddImageWizardAddingProgressPanel(); - AddImageWizardChooseDataSourcePanel dsPanel = new AddImageWizardChooseDataSourcePanel(progressPanel); + AddImageWizardDataSourceSettingsPanel dsPanel = new AddImageWizardDataSourceSettingsPanel(); AddImageWizardIngestConfigPanel ingestConfigPanel = new AddImageWizardIngestConfigPanel(dsPanel, action, progressPanel); panels.add(dsPanel); List profiles = IngestProfiles.getIngestProfiles(); @@ -66,6 +68,7 @@ class AddImageWizardIterator implements WizardDescriptor.Iterator 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.Component; +import java.awt.Cursor; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.logging.Level; +import javax.swing.event.ChangeListener; +import org.openide.WizardDescriptor; +import org.openide.util.HelpCtx; +import org.openide.util.NbBundle; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; +import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescriptorPanel; + +/** + * Create a wizard panel which contains a panel allowing the selection of the + * DataSourceProcessor + */ +final class AddImageWizardSelectDspPanel extends ShortcutWizardDescriptorPanel implements PropertyChangeListener { + + @NbBundle.Messages("SelectDataSourceProcessorPanel.name.text=Select Type of Data") + private AddImageWizardSelectDspVisual component; + private static final String LAST_DSP_PROPERTIES_FILE = "LastDspUsed"; //NON-NLS + private static final String LAST_DSP_USED_KEY = "Last_Dsp_Used"; //NON-NLS + private static final Logger logger = Logger.getLogger(AddImageWizardSelectDspVisual.class.getName()); + + @Override + public Component getComponent() { + if (component == null) { + String lastDspUsed; + if (!(ModuleSettings.getConfigSetting(LAST_DSP_PROPERTIES_FILE, LAST_DSP_USED_KEY) == null) + && !ModuleSettings.getConfigSetting(LAST_DSP_PROPERTIES_FILE, LAST_DSP_USED_KEY).isEmpty()) { + lastDspUsed = ModuleSettings.getConfigSetting(LAST_DSP_PROPERTIES_FILE, LAST_DSP_USED_KEY); + } else { + lastDspUsed = ImageDSProcessor.getType(); + logger.log(Level.WARNING, "There was no properties file containing the last DataSourceProcessor used, Disk Image or VM will be selected as default selection"); //NON-NLS + } + WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + component = new AddImageWizardSelectDspVisual(lastDspUsed); + component.setName(Bundle.SelectDataSourceProcessorPanel_name_text()); + } + component.addPropertyChangeListener(this); + return component; + } + + @Override + public HelpCtx getHelp() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + public void readSettings(WizardDescriptor data) { + } + + @Override + public void storeSettings(WizardDescriptor data) { + String lastDspUsed = component.getSelectedDsp(); + ModuleSettings.setConfigSetting(LAST_DSP_PROPERTIES_FILE, LAST_DSP_USED_KEY, lastDspUsed); + data.putProperty("SelectedDsp", lastDspUsed); //NON-NLS magic string necesary to AddImageWizardChooseDataSourcePanel + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public void addChangeListener(ChangeListener cl) { + } + + @Override + public void removeChangeListener(ChangeListener cl) { + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.form b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.form new file mode 100644 index 0000000000..106c4925c5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.form @@ -0,0 +1,73 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.java new file mode 100644 index 0000000000..7a86e5dfb9 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.java @@ -0,0 +1,241 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.casemodule; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import javax.swing.AbstractButton; +import javax.swing.Box.Filler; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.JToggleButton; +import org.openide.util.Lookup; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.datasourceprocessors.RawDSProcessor; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * Panel which displays the available DataSourceProcessors and allows selection of one + */ +final class AddImageWizardSelectDspVisual extends JPanel { + + private static final Logger logger = Logger.getLogger(AddImageWizardSelectDspVisual.class.getName()); + private String selectedDsp; + + /** + * Creates new form SelectDataSourceProcessorPanel + */ + AddImageWizardSelectDspVisual(String lastDspUsed) { + initComponents(); + selectedDsp = lastDspUsed; + createDataSourceProcessorButtons(); + + //add actionlistner to listen for change + } + + /** + * Find the DSP which is currently selected and save it as the selected + * DataSourceProcessor. + * + */ + private void updateSelectedDsp() { + Enumeration buttonGroup = buttonGroup1.getElements(); + while (buttonGroup.hasMoreElements()) { + AbstractButton dspButton = buttonGroup.nextElement(); + if (dspButton.isSelected()) { + selectedDsp = dspButton.getName(); + break; + } + } + } + + /** + * Get the DataSourceProcessor which is currently selected in this panel + * + * @return selectedDsp the DataSourceProcessor which is selected in this panel + */ + String getSelectedDsp() { + return selectedDsp; + } + + /** + * Create the a button for each DataSourceProcessor that should exist as an option. + */ + private void createDataSourceProcessorButtons() { + //Listener for button selection + ActionListener cbActionListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + updateSelectedDsp(); + } + }; + List dspList = getListOfDsps(); + //Set up the constraints for the panel layout + GridBagLayout gridBagLayout = new GridBagLayout(); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weighty = 0; + constraints.anchor = GridBagConstraints.LINE_START; + Dimension spacerBlockDimension = new Dimension(6, 4); // Space between left edge and button, Space between rows + for (String dspType : dspList) { + constraints.weightx = 1; + //Add a spacer + Filler spacer = new Filler(spacerBlockDimension, spacerBlockDimension, spacerBlockDimension); + gridBagLayout.setConstraints(spacer, constraints); + jPanel1.add(spacer); + constraints.gridx++; + constraints.gridy++; + //Add the button + JToggleButton dspButton = createDspButton(dspType); + dspButton.addActionListener(cbActionListener); + jPanel1.add(dspButton); + buttonGroup1.add(dspButton); + gridBagLayout.setConstraints(dspButton, constraints); + constraints.gridx++; + //Add the text area serving as a label to the right of the button + JTextArea myLabel = new JTextArea(dspType); + myLabel.setBackground(new Color(240, 240, 240));//matches background of panel + myLabel.setEditable(false); + myLabel.setWrapStyleWord(true); + myLabel.setLineWrap(true); + jPanel1.add(myLabel); + gridBagLayout.setConstraints(myLabel, constraints); + constraints.weightx = 0; + constraints.gridy++; + constraints.gridx = 0; + } + Component vertGlue = javax.swing.Box.createVerticalGlue(); + jPanel1.add(vertGlue); + constraints.gridy++; + constraints.gridx = 0; + constraints.weighty = 1; + gridBagLayout.setConstraints(vertGlue, constraints); + jPanel1.setLayout(gridBagLayout); + } + + /** + * Create a list of the DataSourceProcessors which should exist as options on this panel. + * The default Autopsy DataSourceProcessors will appear + * at the beggining of the list in the same order. + * + * @return dspList a list of DataSourceProcessors which can be chose in this panel + */ + private List getListOfDsps() { + List dspList = new ArrayList<>(); + final Map datasourceProcessorsMap = new HashMap<>(); + for (DataSourceProcessor dsProcessor : Lookup.getDefault().lookupAll(DataSourceProcessor.class)) { + if (!datasourceProcessorsMap.containsKey(dsProcessor.getDataSourceType())) { + datasourceProcessorsMap.put(dsProcessor.getDataSourceType(), dsProcessor); + } else { + logger.log(Level.SEVERE, "discoverDataSourceProcessors(): A DataSourceProcessor already exists for type = {0}", dsProcessor.getDataSourceType()); //NON-NLS + } + } + dspList.add(ImageDSProcessor.getType()); + if (Case.getCurrentCase().getCaseType() != Case.CaseType.MULTI_USER_CASE) { + dspList.add(LocalDiskDSProcessor.getType()); + } else { + // remove LocalDiskDSProcessor from list of DSPs + datasourceProcessorsMap.remove(LocalDiskDSProcessor.getType()); + } + dspList.add(LocalFilesDSProcessor.getType()); + dspList.add(RawDSProcessor.getType()); + // now add any addtional DSPs that haven't already been added + for (String dspType : datasourceProcessorsMap.keySet()) { + if (!dspList.contains(dspType)) { + dspList.add(dspType); + } + } + return dspList; + } + + /** + * Create a single button for a DataSourceProcessor + * + * @param dspType - the name of the DataSourceProcessor + * + * @return dspButton a JToggleButton for the specified dspType + */ + private JToggleButton createDspButton(String dspType) { + JToggleButton dspButton = new JToggleButton(); + dspButton.setMaximumSize(new java.awt.Dimension(48, 48)); + dspButton.setMinimumSize(new java.awt.Dimension(48, 48)); + dspButton.setPreferredSize(new java.awt.Dimension(48, 48)); + dspButton.setName(dspType); + dspButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/modules/fileextmismatch/options-icon.png"))); + dspButton.setSelectedIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/checkbox32.png"))); + dspButton.setFocusable(false); + if (dspType.equals(selectedDsp)) { + dspButton.setSelected(true); + } else { + dspButton.setSelected(false); + } + return dspButton; + } + + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + buttonGroup1 = new javax.swing.ButtonGroup(); + jScrollPane1 = new javax.swing.JScrollPane(); + jPanel1 = new javax.swing.JPanel(); + filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(6, 8), new java.awt.Dimension(6, 8), new java.awt.Dimension(6, 8)); + + jPanel1.setLayout(new java.awt.GridBagLayout()); + jPanel1.add(filler1, new java.awt.GridBagConstraints()); + + jScrollPane1.setViewportView(jPanel1); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 588, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 588, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 328, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 328, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.ButtonGroup buttonGroup1; + private javax.swing.Box.Filler filler1; + private javax.swing.JPanel jPanel1; + private javax.swing.JScrollPane jScrollPane1; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index 5e3fa4b6f2..f22ada5664 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -55,8 +55,6 @@ LocalFilesPanel.localFileChooser.approveButtonText=Select LocalFilesPanel.localFileChooser.approveButtonToolTipText= LocalFilesPanel.selectButton.actionCommand=Add AddImageWizardAddingProgressVisual.statusLabel.text=Data source has been added to the local database. Files are being analyzed. -AddImageWizardChooseDataSourceVisual.typeTabel.text=Select data source type: -AddImageWizardChooseDataSourceVisual.jLabel2.text=jLabel2 AddImageWizardAddingProgressVisual.progressLabel.text= AddImageWizardAddingProgressVisual.viewLogButton.text=View Log AddImageWizardAddingProgressVisual.subTitle1Label.text=Processing data source and adding it to a local database. File analysis will start when this finishes. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties index fdb39ad133..82efd7df67 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties @@ -42,8 +42,6 @@ LocalFilesPanel.clearButton.toolTipText=\u73fe\u5728\u9078\u629e\u3055\u308c\u30 LocalFilesPanel.localFileChooser.approveButtonText=\u9078\u629e LocalFilesPanel.selectButton.actionCommand=\u8ffd\u52a0 AddImageWizardAddingProgressVisual.statusLabel.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u304c\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f\u3002\u30d5\u30a1\u30a4\u30eb\u3092\u89e3\u6790\u4e2d\u3067\u3059\u3002 -AddImageWizardChooseDataSourceVisual.typeTabel.text=\u8ffd\u52a0\u3059\u308b\u30bd\u30fc\u30b9\u30bf\u30a4\u30d7\u3092\u9078\u629e\uff1a -AddImageWizardChooseDataSourceVisual.jLabel2.text=jLabel2 AddImageWizardAddingProgressVisual.progressLabel.text=\uff1c\u30d7\u30ed\u30b0\u30ec\u30b9\uff1e AddImageWizardAddingProgressVisual.viewLogButton.text=\u30ed\u30b0\u3092\u8868\u793a AddImageWizardAddingProgressVisual.subTitle1Label.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u60c5\u5831\u3092\u8ffd\u52a0\u4e2d\u3067\u3059\u3002\u3053\u3061\u3089\u304c\u5b8c\u4e86\u6b21\u7b2c\u3001\u30d5\u30a1\u30a4\u30eb\u89e3\u6790\u304c\u59cb\u307e\u308a\u307e\u3059\u3002 diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 70d024777c..e3ab78bf64 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -72,9 +72,9 @@ import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent; import org.sleuthkit.autopsy.casemodule.services.Services; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock; -import org.sleuthkit.autopsy.coordinationservice.CoordinationServiceNamespace; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferencesException; @@ -673,7 +673,7 @@ public class Case { * cannot be deleted if another node has it open. */ progressIndicator.start(Bundle.Case_progressMessage_acquiringLocks()); - try (CoordinationService.Lock dirLock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, metadata.getCaseDirectory())) { + try (CoordinationService.Lock dirLock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, metadata.getCaseDirectory())) { assert (null != dirLock); /* @@ -945,7 +945,7 @@ public class Case { @Messages({"Case.creationException.couldNotAcquireNameLock=Failed to get lock on case name"}) private static CoordinationService.Lock acquireExclusiveCaseNameLock(String caseName) throws CaseActionException { try { - Lock lock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, caseName, NAME_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); + Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, caseName, NAME_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); if (null == lock) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireNameLock()); } @@ -971,7 +971,7 @@ public class Case { private static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseName) throws CaseActionException { try { String resourcesNodeName = caseName + "_resources"; - Lock lock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, resourcesNodeName, RESOURCE_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); + Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, resourcesNodeName, RESOURCE_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); if (null == lock) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock()); } @@ -2337,7 +2337,7 @@ public class Case { @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory."}) private void acquireSharedCaseDirLock(String caseDir) throws CaseActionException { try { - caseDirLock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetSharedLock(CoordinationService.CategoryNode.CASES, caseDir, SHARED_DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); + caseDirLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CASES, caseDir, SHARED_DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); if (null == caseDirLock) { throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock()); } diff --git a/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationService.java b/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationService.java index e546e4d260..36b6dc37e3 100644 --- a/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationService.java +++ b/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationService.java @@ -19,9 +19,13 @@ package org.sleuthkit.autopsy.coordinationservice; import java.io.IOException; -import java.util.HashMap; +import java.util.Collection; +import java.util.Iterator; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; @@ -34,25 +38,28 @@ import org.apache.zookeeper.KeeperException.NoNodeException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; +import org.openide.util.Lookup; import org.sleuthkit.autopsy.core.UserPreferences; /** * A coordination service for maintaining configuration information and * providing distributed synchronization using a shared hierarchical namespace * of nodes. - * - * TODO (JIRA 2205): Simple refactoring for general use. */ +@ThreadSafe public final class CoordinationService { - private static CuratorFramework curator = null; - private static final Map rootNodesToServices = new HashMap<>(); private static final int SESSION_TIMEOUT_MILLISECONDS = 300000; private static final int CONNECTION_TIMEOUT_MILLISECONDS = 300000; private static final int ZOOKEEPER_SESSION_TIMEOUT_MILLIS = 3000; private static final int ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS = 15000; private static final int PORT_OFFSET = 1000; // When run in Solr, ZooKeeper defaults to Solr port + 1000 - private final Map categoryNodeToPath = new HashMap<>(); + private static final String DEFAULT_NAMESPACE_ROOT = "autopsy"; + @GuardedBy("CoordinationService.class") + private static CoordinationService instance; + private final CuratorFramework curator; + @GuardedBy("categoryNodeToPath") + private final Map categoryNodeToPath; /** * Determines if ZooKeeper is accessible with the current settings. Closes @@ -86,44 +93,31 @@ public final class CoordinationService { } /** - * Gets a coordination service for a specific namespace. + * Gets the coordination service for maintaining configuration information + * and providing distributed synchronization using a shared hierarchical + * namespace of nodes. * - * @param rootNode The name of the root node that defines the namespace. + * @return The corrdination service. * - * @return The coordination service. - * - * @throws CoordinationServiceException If an instance of the coordination - * service cannot be created. + * @throws CoordinationServiceException */ - public static synchronized CoordinationService getServiceForNamespace(String rootNode) throws CoordinationServiceException { - /* - * Connect to ZooKeeper via Curator. - */ - if (null == curator) { - RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); - int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET; - String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort; - curator = CuratorFrameworkFactory.newClient(connectString, SESSION_TIMEOUT_MILLISECONDS, CONNECTION_TIMEOUT_MILLISECONDS, retryPolicy); - curator.start(); - } - - /* - * Get or create a coordination service for the namespace defined by the - * specified root node. - */ - if (rootNodesToServices.containsKey(rootNode)) { - return rootNodesToServices.get(rootNode); - } else { - CoordinationService service; + public synchronized static CoordinationService getInstance() throws CoordinationServiceException { + if (null == instance) { + String rootNode; + Collection providers = Lookup.getDefault().lookupAll(CoordinationServiceNamespace.class); + Iterator it = providers.iterator(); + if (it.hasNext()) { + rootNode = it.next().getNamespaceRoot(); + } else { + rootNode = DEFAULT_NAMESPACE_ROOT; + } try { - service = new CoordinationService(rootNode); + instance = new CoordinationService(rootNode); } catch (IOException | InterruptedException | KeeperException | CoordinationServiceException ex) { - curator = null; throw new CoordinationServiceException("Failed to create coordination service", ex); } - rootNodesToServices.put(rootNode, service); - return service; } + return instance; } /** @@ -141,11 +135,23 @@ public final class CoordinationService { throw new CoordinationServiceException("Unable to access ZooKeeper"); } + /* + * Connect to ZooKeeper via Curator. + */ + RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); + int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET; + String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort; + curator = CuratorFrameworkFactory.newClient(connectString, SESSION_TIMEOUT_MILLISECONDS, CONNECTION_TIMEOUT_MILLISECONDS, retryPolicy); + curator.start(); + + /* + * Create the top-level root and category nodes. + */ String rootNode = rootNodeName; if (!rootNode.startsWith("/")) { rootNode = "/" + rootNode; } - + categoryNodeToPath = new ConcurrentHashMap<>(); for (CategoryNode node : CategoryNode.values()) { String nodePath = rootNode + "/" + node.getDisplayName(); try { @@ -422,8 +428,7 @@ public final class CoordinationService { CASES("cases"), MANIFESTS("manifests"), - CONFIG("config"), - RESOURCE("resource"); + CONFIG("config"); private final String displayName; diff --git a/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationServiceNamespace.java b/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationServiceNamespace.java index 567dd38bc6..3ffe0b78fd 100644 --- a/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationServiceNamespace.java +++ b/Core/src/org/sleuthkit/autopsy/coordinationservice/CoordinationServiceNamespace.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2016-2017 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,15 +19,18 @@ package org.sleuthkit.autopsy.coordinationservice; /** - * Root node for Autopsy coordination service namespace. + * An interface that allows the root node of the coordination service namespace + * for the application to be specified at runtime. An application built on the + * Autopsy platform should provide at most one implementation of this interface + * (additional implementations are ignored). */ -public final class CoordinationServiceNamespace { - private static final String ROOT = "autopsy"; +public interface CoordinationServiceNamespace { - public static String getRoot() { - return ROOT; - } + /** + * Gets the name of the root node of the coordination service namespace. + * + * @return The name of the root node. + */ + public String getNamespaceRoot(); - private CoordinationServiceNamespace() { - } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java index 3559bb79d7..eb736069a7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java @@ -51,7 +51,9 @@ abstract class AbstractContentChildren extends Keys { * Uses lazy Content.Keys */ AbstractContentChildren() { - super(true); // use lazy behavior + /*This was turned off because we were getting out of memory errors when the + filter nodes were hiding nodes. Turning this off seemed to help */ + super(false); //don't use lazy behavior } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java index 7c28e09529..304febdf74 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java @@ -61,7 +61,7 @@ public class ArtifactStringContent implements StringContent { * @return The HTML representation of the artifact as a string. */ @Messages({ - "ArtifactStringContent.attrsTableHeader.attribute=Attribute", + "ArtifactStringContent.attrsTableHeader.type=Type", "ArtifactStringContent.attrsTableHeader.value=Value", "ArtifactStringContent.attrsTableHeader.sources=Source(s)", "ArtifactStringContent.failedToGetSourcePath.message=Failed to get source file path from case database", @@ -90,15 +90,15 @@ public class ArtifactStringContent implements StringContent { */ buffer.append(""); //NON-NLS buffer.append(""); //NON-NLS - buffer.append(""); //NON-NLS - buffer.append(""); //NON-NLS + buffer.append(""); //NON-NLS - buffer.append(""); //NON-NLS + buffer.append(""); //NON-NLS + buffer.append(""); //NON-NLS buffer.append("\n"); //NON-NLS try { Content content = artifact.getSleuthkitCase().getContentById(artifact.getObjectID()); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java index a154fcb6db..8b1c81a19a 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java @@ -25,6 +25,7 @@ import java.awt.event.ActionEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -413,19 +414,34 @@ public final class IngestJobSettingsPanel extends javax.swing.JPanel { FilesSetDefsPanel fileIngestFilterPanel; fileIngestFilterPanel = new FilesSetDefsPanel(FilesSetDefsPanel.PANEL_TYPE.FILE_INGEST_FILTERS); fileIngestFilterPanel.load(); + //save the filters that exist before any are created + final ArrayList oldFilterList = new ArrayList<>(Arrays.asList(getComboBoxContents())); dialog.addApplyButtonListener( (ActionEvent e) -> { fileIngestFilterPanel.store(); + ArrayList newFilterList = new ArrayList<>(); + try { + newFilterList.addAll(FilesSetsManager.getInstance().getCustomFileIngestFilters().values()); + } catch (FilesSetsManager.FilesSetsManagerException ex) { + logger.log(Level.SEVERE, "Failed to get user created file ingest filters, only default available for selection", ex); //NON-NLS + } + for (FilesSet filter : newFilterList) { //getting one of the recently created filters + if (!oldFilterList.contains(filter.getName())) { + //set newly created filter to selected filter + settings.setFileIngestFilter(filter); + break; + } + } fileIngestFilterComboBox.setModel(new DefaultComboBoxModel<>(getComboBoxContents())); + //set the selected filter after the comboBox Contents were updated to include it + fileIngestFilterComboBox.setSelectedItem(settings.getFileIngestFilter().getName()); dialog.close(); } ); dialog.display(fileIngestFilterPanel); - + //return to saved selection in case they cancel out of filter creation fileIngestFilterComboBox.setSelectedItem(settings.getFileIngestFilter().getName()); - } else if (evt.getActionCommand().equals("comboBoxChanged")) { - try { Map fileIngestFilters = FilesSetsManager.getInstance() .getCustomFileIngestFilters(); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestOptionsPanel.java index 28bf7a623e..d10b987873 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestOptionsPanel.java @@ -47,6 +47,7 @@ public class IngestOptionsPanel extends IngestModuleGlobalSettingsPanel implemen private final static int INDEX_OF_SETTINGS_PANEL = 2; private ProfileSettingsPanel profilePanel; private final static int INDEX_OF_PROFILE_PANEL = 1; + private int indexOfPreviousTab; /** * This panel implements a property change listener that listens to ingest * job events so it can disable the buttons on the panel if ingest is @@ -58,6 +59,7 @@ public class IngestOptionsPanel extends IngestModuleGlobalSettingsPanel implemen public IngestOptionsPanel() { initComponents(); customizeComponents(); + indexOfPreviousTab = tabbedPane.getSelectedIndex(); } private void customizeComponents() { @@ -78,15 +80,16 @@ public class IngestOptionsPanel extends IngestModuleGlobalSettingsPanel implemen @Override public void stateChanged(ChangeEvent e) { if (e.getSource() instanceof JTabbedPane) { - //because we can have two filterPanels open at the same time - //we need to save the settings when we change tabs otherwise - //they could be overwritten with out of date - if (tabbedPane.getSelectedIndex() == INDEX_OF_FILTER_PANEL) { - filterPanel.load(); - } - else { - filterPanel.saveSettings(); - } + //If we are switching to a filter panel we should load + //load the filter panel to ensure it is up to date + //incase a filter was addded through the Profile->new->create new filter manner + if (tabbedPane.getSelectedIndex() == INDEX_OF_FILTER_PANEL && tabbedPane.getSelectedIndex() != indexOfPreviousTab) { + filterPanel.load(); + } + //save the contents of whichever Tab we just switched from + saveTabByIndex(indexOfPreviousTab); + //save the index of the current tab for the next time we switch + indexOfPreviousTab = tabbedPane.getSelectedIndex(); } } }); @@ -160,8 +163,30 @@ public class IngestOptionsPanel extends IngestModuleGlobalSettingsPanel implemen */ @Override public void saveSettings() { - filterPanel.store(); - settingsPanel.store(); + saveTabByIndex(tabbedPane.getSelectedIndex()); + } + + /** + * Save the panel which is in the tab corresponding to the specified index. + * + * @param index - the index of the tab you wish to save the contents of + */ + private void saveTabByIndex(int index) { + //Because we can create filters in two seperate windows here we need + //to be careful not to save an out of date list over the current list + switch (index) { + case (INDEX_OF_FILTER_PANEL): + filterPanel.saveSettings(); + break; + case (INDEX_OF_PROFILE_PANEL): + profilePanel.saveSettings(); + break; + case (INDEX_OF_SETTINGS_PANEL): + settingsPanel.saveSettings(); + break; + default: + //don't save anything if it wasn't a tab index that should exist + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.form index 6ced55aa7e..5b7b56cdf8 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.form @@ -27,22 +27,28 @@ - + - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -60,7 +66,7 @@ - + @@ -97,13 +103,10 @@ - - - - - - + + + @@ -115,9 +118,16 @@ - + + + + + + + + + - @@ -128,7 +138,7 @@ - + @@ -355,5 +365,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java index 1be76f8ecc..94fd0626e5 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java @@ -27,7 +27,6 @@ import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.netbeans.spi.options.OptionsPanelController; import org.openide.util.NbBundle; -import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.ingest.IngestProfiles.IngestProfile; @@ -46,12 +45,15 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op "ProfileSettingsPanel.deleteProfileButton.text=Delete Profile", "ProfileSettingsPanel.messages.filterLoadFailed=Failed to load file ingest filter", "# {0} - profile name", - "ProfileSettingsPanel.doFileSetsDialog.duplicateProfile.text=Profile with name {0} already exists." + "ProfileSettingsPanel.doFileSetsDialog.duplicateProfile.text=Profile with name {0} already exists.", + "ProfileSettingsPanel.infoTextArea.text=An Ingest Profile runs a preconfigured set of ingest modules" + + " on some or all of the files in a data source. Create a profile if you frequently run the same set of modules on a subset of the files." }) private final DefaultListModel profilesListModel; private Map profiles; private ProfilePanel panel; + private boolean canBeEnabled; //if something can be enabled ingest is not running /** * Creates new form ProfileOptionsPanel @@ -62,8 +64,8 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op this.profileList.setModel(profilesListModel); this.profileList.addListSelectionListener(new ProfileSettingsPanel.ProfileListSelectionListener()); ingestWarningLabel.setVisible(false); - editProfileButton.setEnabled(false); - deleteProfileButton.setEnabled(false); + canBeEnabled = !IngestManager.getInstance().isIngestRunning(); + refreshButtons(); } /** @@ -93,6 +95,8 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op selectedModulesLabel = new javax.swing.JLabel(); ingestWarningLabel = new javax.swing.JLabel(); jSeparator2 = new javax.swing.JSeparator(); + jScrollPane2 = new javax.swing.JScrollPane(); + infoTextArea = new javax.swing.JTextArea(); setBorder(javax.swing.BorderFactory.createEtchedBorder()); setPreferredSize(new java.awt.Dimension(800, 488)); @@ -177,6 +181,18 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op jSeparator2.setOrientation(javax.swing.SwingConstants.VERTICAL); + jScrollPane2.setFont(jScrollPane2.getFont().deriveFont(jScrollPane2.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + + infoTextArea.setEditable(false); + infoTextArea.setBackground(new java.awt.Color(240, 240, 240)); + infoTextArea.setColumns(20); + infoTextArea.setFont(infoTextArea.getFont().deriveFont(infoTextArea.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + infoTextArea.setLineWrap(true); + infoTextArea.setRows(3); + infoTextArea.setText(org.openide.util.NbBundle.getMessage(ProfileSettingsPanel.class, "ProfileSettingsPanel.infoTextArea.text")); // NOI18N + infoTextArea.setWrapStyleWord(true); + jScrollPane2.setViewportView(infoTextArea); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -184,17 +200,21 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(profileListLabel) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(newProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(editProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(deleteProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, 109, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(jScrollPane2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 346, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(profileListLabel)) + .addGap(6, 6, 6)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(profileListPane, javax.swing.GroupLayout.PREFERRED_SIZE, 339, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(newProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(editProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(deleteProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, 109, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGap(6, 6, 6))) + .addComponent(profileListPane, javax.swing.GroupLayout.PREFERRED_SIZE, 346, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED))) .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() @@ -210,7 +230,7 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(ingestWarningLabel) - .addGap(0, 0, Short.MAX_VALUE)) + .addGap(0, 69, Short.MAX_VALUE)) .addComponent(profileDescPane, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(selectedModulesPane, javax.swing.GroupLayout.Alignment.TRAILING))) .addGroup(layout.createSequentialGroup() @@ -231,18 +251,18 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {deleteProfileButton, editProfileButton, newProfileButton}); + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {jScrollPane2, profileListPane}); + layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(profileListLabel) - .addComponent(profileDescLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() + .addComponent(profileDescLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(profileDescPane, 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) @@ -253,8 +273,14 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(selectedModulesLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(selectedModulesPane)) - .addComponent(profileListPane, javax.swing.GroupLayout.DEFAULT_SIZE, 415, Short.MAX_VALUE)) + .addComponent(selectedModulesPane, javax.swing.GroupLayout.DEFAULT_SIZE, 171, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(profileListLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(profileListPane, javax.swing.GroupLayout.DEFAULT_SIZE, 346, Short.MAX_VALUE) + .addGap(9, 9, 9))) .addGap(4, 4, 4) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(newProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -262,7 +288,7 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op .addComponent(deleteProfileButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(ingestWarningLabel)) .addContainerGap()) - .addComponent(jSeparator2, javax.swing.GroupLayout.Alignment.TRAILING))) + .addComponent(jSeparator2))) ); }// //GEN-END:initComponents @@ -290,10 +316,9 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op * @param isEnabled */ void enableButtons(boolean isEnabled) { - newProfileButton.setEnabled(isEnabled); - editProfileButton.setEnabled(isEnabled); - deleteProfileButton.setEnabled(isEnabled); - ingestWarningLabel.setVisible(!isEnabled); + canBeEnabled = isEnabled; //update value of canBeEnabled to be used by refresh + refreshButtons(); + ingestWarningLabel.setVisible(!canBeEnabled); } /** @@ -309,23 +334,20 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op this.filterNameText.setText(""); this.selectedModulesArea.setText(""); } - refreshEditDeleteButtons(); + refreshButtons(); } /** - * When Ingest is not running this will changed enabled status of the edit - * and delete buttons to reflect their current availability. + * When Ingest is not running this will change the enabled status of the + * edit and delete buttons to reflect their current availability. */ - private void refreshEditDeleteButtons() { - if (newProfileButton.isEnabled()) { - if (profilesListModel.isEmpty()) { - editProfileButton.setEnabled(false); - deleteProfileButton.setEnabled(false); - } else { - editProfileButton.setEnabled(true); - deleteProfileButton.setEnabled(true); - } - } + private void refreshButtons() { + IngestProfile selectedProfile = ProfileSettingsPanel.this.profileList.getSelectedValue(); + boolean profileIsSelected = (selectedProfile != null); + newProfileButton.setEnabled(canBeEnabled); + editProfileButton.setEnabled(canBeEnabled && profileIsSelected); + deleteProfileButton.setEnabled(canBeEnabled && profileIsSelected); + } @@ -372,7 +394,6 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op panel.saveSettings(); load(); } - } @Override @@ -398,7 +419,7 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op if (currentIndex < 0 || currentIndex >= profilesListModel.getSize()) { currentIndex = 0; } - refreshEditDeleteButtons(); + refreshButtons(); this.profileList.setSelectedIndex(currentIndex); } @@ -412,11 +433,10 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op // Get the selected interesting files set and populate the set // components. IngestProfile selectedProfile = ProfileSettingsPanel.this.profileList.getSelectedValue(); + refreshButtons(); if (selectedProfile != null) { profileDescArea.setText(selectedProfile.getDescription()); filterNameText.setText(selectedProfile.getFileIngestFilter()); - editProfileButton.setEnabled(true); - deleteProfileButton.setEnabled(true); try { Map fileIngestFilters = FilesSetsManager.getInstance().getCustomFileIngestFilters(); for (FilesSet fSet : FilesSetsManager.getStandardFileIngestFilters()) { @@ -430,10 +450,6 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op for (String moduleName : IngestJobSettings.getEnabledModules(selectedProfile.getName())) { selectedModulesArea.append(moduleName + "\n"); } - - } else { - editProfileButton.setEnabled(false); - deleteProfileButton.setEnabled(false); } } } @@ -445,7 +461,9 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op private javax.swing.JScrollPane filterDescPane; private javax.swing.JLabel filterNameLabel; private javax.swing.JLabel filterNameText; + private javax.swing.JTextArea infoTextArea; private javax.swing.JLabel ingestWarningLabel; + private javax.swing.JScrollPane jScrollPane2; private javax.swing.JSeparator jSeparator2; private javax.swing.JButton newProfileButton; private javax.swing.JTextArea profileDescArea; diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index 4b643e2a02..059ac8530f 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -855,6 +855,8 @@ class ReportHTML implements TableReportModule { if (iconPath == null) { // use default Autopsy icon if custom icon is not set iconPath = "favicon.ico"; + } else { + iconPath = "agency_logo"; //ref to writeNav() for agency_logo } index.append("\n").append(reportTitle).append(" ").append( NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.title", currentCase.getDisplayName())).append( diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobLogger.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobLogger.java index 161a20286e..817fbac6a0 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobLogger.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobLogger.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2011 - 2017 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.experimental.autoingest; -import org.sleuthkit.autopsy.coordinationservice.CoordinationServiceNamespace; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; @@ -29,15 +28,12 @@ import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.time.Instant; import java.util.Date; -import org.sleuthkit.autopsy.coreutils.NetworkUtils; -import org.sleuthkit.autopsy.coordinationservice.CoordinationService; -import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock; -import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; import java.util.concurrent.TimeUnit; -import java.util.List; import javax.annotation.concurrent.Immutable; -import org.sleuthkit.autopsy.ingest.IngestModuleError; -import org.sleuthkit.autopsy.ingest.IngestManager.IngestManagerException; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock; +import org.sleuthkit.autopsy.coreutils.NetworkUtils; /** * A logger for the processing of an auto ingest job by an auto ingest node. An @@ -107,14 +103,16 @@ final class AutoIngestJobLogger { * Advanced users doing troubleshooting of an automated ingest cluster * should also consult the Autopsy and system logs as needed. * - * @param manifestPath The manifest for the auto ingest job. - * @param caseDirectoryPath The case directory. + * @param manifestPath The manifest for the auto ingest job. + * @param dataSourceFileName The file name of the data source for the auto + * ingest job. + * @param caseDirectoryPath The absolute path to the case directory. */ AutoIngestJobLogger(Path manifestPath, String dataSourceFileName, Path caseDirectoryPath) { this.manifestPath = manifestPath; manifestFileName = manifestPath.getFileName().toString(); this.dataSourceFileName = dataSourceFileName; - this.caseDirectoryPath = caseDirectoryPath; + this.caseDirectoryPath = caseDirectoryPath; hostName = NetworkUtils.getLocalHostName(); } @@ -195,30 +193,34 @@ final class AutoIngestJobLogger { void logDataSourceProcessorCancelled() throws AutoIngestJobLoggerException, InterruptedException { log(MessageCategory.WARNING, "Cancelled adding data source to case"); } - + /** * Logs selection of a data source processor - * @param dsp Name of the data source processor + * + * @param dsp Name of the data source processor + * * @throws AutoIngestJobLoggerException if there is an error writing the log * message. * @throws InterruptedException if interrupted while blocked waiting * to acquire an exclusive lock on the * log file. */ - void logDataSourceProcessorSelected(String dsp) throws AutoIngestJobLoggerException, InterruptedException{ + void logDataSourceProcessorSelected(String dsp) throws AutoIngestJobLoggerException, InterruptedException { log(MessageCategory.INFO, "Using data source processor: " + dsp); } - + /** * Logs the failure of the selected data source processor. - * @param dsp Name of the data source processor + * + * @param dsp Name of the data source processor + * * @throws AutoIngestJobLoggerException if there is an error writing the log * message. * @throws InterruptedException if interrupted while blocked waiting * to acquire an exclusive lock on the * log file. */ - void logDataSourceProcessorError(String dsp) throws AutoIngestJobLoggerException, InterruptedException{ + void logDataSourceProcessorError(String dsp) throws AutoIngestJobLoggerException, InterruptedException { log(MessageCategory.ERROR, "Error processing with data source processor: " + dsp); } @@ -431,9 +433,10 @@ final class AutoIngestJobLogger { * log file. */ private void log(MessageCategory category, String message) throws AutoIngestJobLoggerException, InterruptedException { - try (Lock lock = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, getLogPath(caseDirectoryPath).toString(), LOCK_TIME_OUT, LOCK_TIME_OUT_UNIT)) { + Path logPath = getLogPath(caseDirectoryPath); + try (Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, logPath.toString(), LOCK_TIME_OUT, LOCK_TIME_OUT_UNIT)) { if (null != lock) { - File logFile = getLogPath(caseDirectoryPath).toFile(); + File logFile = logPath.toFile(); try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(logFile, logFile.exists())), true)) { writer.println(String.format("%s %s: %s: %s: %-8s: %s", logDateFormat.format((Date.from(Instant.now()).getTime())), hostName, manifestFileName, dataSourceFileName, category.toString(), message)); } catch (IOException ex) { diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index eb45ae0df4..07254c247c 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -73,14 +73,11 @@ import org.sleuthkit.autopsy.casemodule.CaseMetadata; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock; -import org.sleuthkit.autopsy.coordinationservice.CoordinationServiceNamespace; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.core.ServicesMonitor; import org.sleuthkit.autopsy.core.ServicesMonitor.ServicesMonitorException; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferencesException; -import org.sleuthkit.autopsy.framework.AutoIngestDataSourceProcessor; -import org.sleuthkit.autopsy.framework.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; @@ -100,6 +97,8 @@ import static org.sleuthkit.autopsy.experimental.autoingest.ManifestNodeData.Pro import org.sleuthkit.autopsy.experimental.configuration.AutoIngestUserPreferences; import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration; import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration.SharedConfigurationException; +import org.sleuthkit.autopsy.framework.AutoIngestDataSourceProcessor; +import org.sleuthkit.autopsy.framework.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestJob.CancellationReason; import org.sleuthkit.autopsy.ingest.IngestJobSettings; @@ -212,7 +211,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang void startUp() throws AutoIngestManagerStartupException { SYS_LOGGER.log(Level.INFO, "Auto ingest starting"); try { - coordinationService = CoordinationService.getServiceForNamespace(CoordinationServiceNamespace.getRoot()); + coordinationService = CoordinationService.getInstance(); } catch (CoordinationServiceException ex) { throw new AutoIngestManagerStartupException("Failed to get coordination service", ex); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/SharedConfiguration.java b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/SharedConfiguration.java index 653fc65807..227c203177 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/SharedConfiguration.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/SharedConfiguration.java @@ -50,6 +50,7 @@ import org.sleuthkit.autopsy.core.ServicesMonitor; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; import org.sleuthkit.autopsy.experimental.configuration.AutoIngestSettingsPanel.UpdateConfigSwingWorker; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; @@ -86,7 +87,6 @@ public class SharedConfiguration { private static final String PREFERENCES_FOLDER = "Preferences"; //NON-NLS public static final String FILE_EXPORTER_FOLDER = "Automated File Exporter"; //NON-NLS - private static final String LOCK_ROOT = "/autopsy"; // NON-NLS private static final String UPLOAD_IN_PROGRESS_FILE = "uploadInProgress"; // NON-NLS private static final String moduleDirPath = PlatformUtil.getUserConfigDirectory(); private static final Logger logger = Logger.getLogger(SharedConfiguration.class.getName()); @@ -160,7 +160,7 @@ public class SharedConfiguration { File remoteFolder = getSharedFolder(); - try (Lock writeLock = CoordinationService.getServiceForNamespace(LOCK_ROOT).tryGetExclusiveLock(CoordinationService.CategoryNode.CONFIG, remoteFolder.getAbsolutePath(), 30, TimeUnit.MINUTES)) { + try (Lock writeLock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CONFIG, remoteFolder.getAbsolutePath(), 30, TimeUnit.MINUTES)) { if (writeLock == null) { logger.log(Level.INFO, String.format("Failed to lock %s - another node is currently uploading or downloading configuration", remoteFolder.getAbsolutePath())); return SharedConfigResult.LOCKED; @@ -230,7 +230,7 @@ public class SharedConfiguration { File remoteFolder = getSharedFolder(); - try (Lock readLock = CoordinationService.getServiceForNamespace(LOCK_ROOT).tryGetSharedLock(CoordinationService.CategoryNode.CONFIG, remoteFolder.getAbsolutePath(), 30, TimeUnit.MINUTES)) { + try (Lock readLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CONFIG, remoteFolder.getAbsolutePath(), 30, TimeUnit.MINUTES)) { if (readLock == null) { return SharedConfigResult.LOCKED; } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java index 83f2179132..9aa8b74a4f 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java @@ -27,7 +27,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; -import java.util.TreeSet; import java.util.logging.Level; import java.util.stream.Collectors; import javax.annotation.concurrent.GuardedBy; @@ -244,7 +243,6 @@ class HighlightedText implements IndexedText { @Override public boolean hasNextPage() { return getIndexOfCurrentPage() < pages.size() - 1; - } @Override @@ -255,7 +253,7 @@ class HighlightedText implements IndexedText { @Override public int nextPage() { if (hasNextPage()) { - currentPage = Iterators.get(pages.iterator(),getIndexOfCurrentPage() + 1); + currentPage = Iterators.get(pages.iterator(), getIndexOfCurrentPage() + 1); return currentPage; } else { throw new IllegalStateException("No next page."); @@ -265,7 +263,7 @@ class HighlightedText implements IndexedText { @Override public int previousPage() { if (hasPreviousPage()) { - currentPage = Iterators.get(pages.iterator(),getIndexOfCurrentPage()-1); + currentPage = Iterators.get(pages.iterator(), getIndexOfCurrentPage() - 1); return currentPage; } else { throw new IllegalStateException("No previous page."); @@ -387,7 +385,7 @@ class HighlightedText implements IndexedText { highlightedContent = insertAnchors(highlightedContent); return "<html><pre>" + highlightedContent + "</pre></html>"; //NON-NLS - } catch (Exception ex) { + } catch (TskCoreException | KeywordSearchModuleException | NoOpenCoreException ex) { logger.log(Level.SEVERE, "Error getting highlighted text for " + objectId, ex); //NON-NLS return NbBundle.getMessage(this.getClass(), "HighlightedMatchesSource.getMarkup.queryFailedMsg"); } @@ -453,8 +451,8 @@ class HighlightedText implements IndexedText { //we also need to escape the keyword so that it matches the escpared text final String escapedKeyword = StringEscapeUtils.escapeHtml(keyword); int textOffset = 0; - int hitOffset; - while ((hitOffset = StringUtils.indexOfIgnoreCase(text, escapedKeyword, textOffset)) != -1) { + int hitOffset = StringUtils.indexOfIgnoreCase(text, escapedKeyword, textOffset); + while (hitOffset != -1) { // Append the portion of text up to (but not including) the hit. highlightedText.append(text.substring(textOffset, hitOffset)); // Add in the highlighting around the keyword. @@ -464,13 +462,15 @@ class HighlightedText implements IndexedText { // Advance the text offset past the keyword. textOffset = hitOffset + escapedKeyword.length(); + + hitOffset = StringUtils.indexOfIgnoreCase(text, escapedKeyword, textOffset); } // Append the remainder of text field highlightedText.append(text.substring(textOffset, text.length())); - + if (highlightedText.length() == 0) { return NbBundle.getMessage(HighlightedText.class, "HighlightedMatchesSource.getMarkup.noMatchMsg"); - } + } //reset for next pass text = highlightedText.toString(); highlightedText = new StringBuilder(""); @@ -487,22 +487,21 @@ class HighlightedText implements IndexedText { * @return */ private String insertAnchors(String searchableContent) { - int searchOffset = 0; - int index = -1; - StringBuilder buf = new StringBuilder(searchableContent); - final String searchToken = HIGHLIGHT_PRE; final int indexSearchTokLen = searchToken.length(); final String insertPre = "<a name='" + ANCHOR_PREFIX; //NON-NLS final String insertPost = "'></a>"; //NON-NLS int count = 0; - while ((index = buf.indexOf(searchToken, searchOffset)) >= 0) { + int searchOffset = 0; + int index = buf.indexOf(searchToken, searchOffset); + while (index >= 0) { String insertString = insertPre + Integer.toString(count + 1) + insertPost; int insertStringLen = insertString.length(); buf.insert(index, insertString); searchOffset = index + indexSearchTokLen + insertStringLen; //next offset past this anchor ++count; + index = buf.indexOf(searchToken, searchOffset); } //store total hits for this page, now that we know it diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java index 789f238329..dcd485582c 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java @@ -27,17 +27,23 @@ import java.util.logging.Level; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.SolrRequest.METHOD; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; +import org.apache.solr.common.params.CursorMarkParams; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.Version; +import static org.sleuthkit.autopsy.keywordsearch.RegexQuery.LOGGER; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; /** @@ -54,7 +60,7 @@ class LuceneQuery implements KeywordSearchQuery { private KeywordList keywordList = null; private final List<KeywordQueryFilter> filters = new ArrayList<>(); private String field = null; - private static final int MAX_RESULTS = 20000; + private static final int MAX_RESULTS_PER_CURSOR_MARK = 512; static final int SNIPPET_LENGTH = 50; static final String HIGHLIGHT_FIELD = Server.Schema.TEXT.toString(); @@ -65,7 +71,7 @@ class LuceneQuery implements KeywordSearchQuery { * * @param keyword */ - public LuceneQuery(KeywordList keywordList, Keyword keyword) { + LuceneQuery(KeywordList keywordList, Keyword keyword) { this.keywordList = keywordList; this.originalKeyword = keyword; @@ -89,7 +95,7 @@ class LuceneQuery implements KeywordSearchQuery { public void setSubstringQuery() { // Note that this is not a full substring search. Normally substring // searches will be done with TermComponentQuery objects instead. - keywordStringEscaped = keywordStringEscaped + "*"; + keywordStringEscaped += "*"; } @Override @@ -120,17 +126,17 @@ class LuceneQuery implements KeywordSearchQuery { @Override public QueryResults performQuery() throws KeywordSearchModuleException, NoOpenCoreException { - QueryResults results = new QueryResults(this, keywordList); + QueryResults results = new QueryResults(this); //in case of single term literal query there is only 1 term - boolean showSnippets = KeywordSearchSettings.getShowSnippets(); - results.addResult(new Keyword(keywordString, true), performLuceneQuery(showSnippets)); + results.addResult(new Keyword(keywordString, true), + performLuceneQuery(KeywordSearchSettings.getShowSnippets())); return results; } @Override public boolean validate() { - return keywordString != null && !keywordString.equals(""); + return StringUtils.isNotBlank(keywordString); } @Override @@ -180,7 +186,7 @@ class LuceneQuery implements KeywordSearchQuery { bba.addAttributes(attributes); //write out to bb writeResult.add(attributes); return writeResult; - } catch (TskException e) { + } catch (TskCoreException e) { logger.log(Level.WARNING, "Error adding bb attributes to artifact", e); //NON-NLS } return null; @@ -191,46 +197,41 @@ class LuceneQuery implements KeywordSearchQuery { * * @param snippets True if results should have a snippet * - * @return list of ContentHit objects. One per file with hit (ignores + * @return list of KeywordHit objects. One per file with hit (ignores * multiple hits of the word in the same doc) * - * @throws NoOpenCoreException */ private List<KeywordHit> performLuceneQuery(boolean snippets) throws KeywordSearchModuleException, NoOpenCoreException { List<KeywordHit> matches = new ArrayList<>(); - boolean allMatchesFetched = false; final Server solrServer = KeywordSearch.getServer(); + double indexSchemaVersion = NumberUtils.toDouble(solrServer.getIndexInfo().getSchemaVersion()); - SolrQuery q = createAndConfigureSolrQuery(snippets); - QueryResponse response; - SolrDocumentList resultList; - Map<String, Map<String, List<String>>> highlightResponse; - - response = solrServer.query(q, METHOD.POST); - - resultList = response.getResults(); - // objectId_chunk -> "text" -> List of previews - highlightResponse = response.getHighlighting(); + SolrQuery solrQuery = createAndConfigureSolrQuery(snippets); final String strippedQueryString = StringUtils.strip(getQueryString(), "\""); - // cycle through results in sets of MAX_RESULTS - for (int start = 0; !allMatchesFetched; start = start + MAX_RESULTS) { - q.setStart(start); + String cursorMark = CursorMarkParams.CURSOR_MARK_START; + boolean allResultsProcessed = false; - allMatchesFetched = start + MAX_RESULTS >= resultList.getNumFound(); + while (!allResultsProcessed) { + solrQuery.set(CursorMarkParams.CURSOR_MARK_PARAM, cursorMark); + QueryResponse response = solrServer.query(solrQuery, SolrRequest.METHOD.POST); + SolrDocumentList resultList = response.getResults(); + // objectId_chunk -> "text" -> List of previews + Map<String, Map<String, List<String>>> highlightResponse = response.getHighlighting(); for (SolrDocument resultDoc : resultList) { try { - /* for each result, check that the first occurence of that + /* + * for each result, check that the first occurence of that * term is before the window. if all the ocurences start * within the window, don't record them for this chunk, they - * will get picked up in the next one. */ + * will get picked up in the next one. + */ final String docId = resultDoc.getFieldValue(Server.Schema.ID.toString()).toString(); final Integer chunkSize = (Integer) resultDoc.getFieldValue(Server.Schema.CHUNK_SIZE.toString()); final Collection<Object> content = resultDoc.getFieldValues(Server.Schema.CONTENT_STR.toString()); - double indexSchemaVersion = NumberUtils.toDouble(KeywordSearch.getServer().getIndexInfo().getSchemaVersion()); if (indexSchemaVersion < 2.0) { //old schema versions don't support chunk_size or the content_str fields, so just accept hits matches.add(createKeywordtHit(highlightResponse, docId)); @@ -250,11 +251,17 @@ class LuceneQuery implements KeywordSearchQuery { return matches; } } + String nextCursorMark = response.getNextCursorMark(); + if (cursorMark.equals(nextCursorMark)) { + allResultsProcessed = true; + } + cursorMark = nextCursorMark; + } return matches; } - /** + /* * Create the query object for the stored keyword * * @param snippets True if query should request snippets @@ -271,17 +278,17 @@ class LuceneQuery implements KeywordSearchQuery { // Run the query against an optional alternative field. if (field != null) { //use the optional field - StringBuilder sb = new StringBuilder(); - sb.append(field).append(":").append(theQueryStr); - theQueryStr = sb.toString(); + theQueryStr = field + ":" + theQueryStr; } q.setQuery(theQueryStr); - q.setRows(MAX_RESULTS); + q.setRows(MAX_RESULTS_PER_CURSOR_MARK); + // Setting the sort order is necessary for cursor based paging to work. + q.setSort(SolrQuery.SortClause.asc(Server.Schema.ID.toString())); q.setFields(Server.Schema.ID.toString(), Server.Schema.CHUNK_SIZE.toString(), Server.Schema.CONTENT_STR.toString()); - q.addSort(Server.Schema.ID.toString(), SolrQuery.ORDER.asc); + for (KeywordQueryFilter filter : filters) { q.addFilterQuery(filter.toString()); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java index 8d47831dba..410ec1a94c 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java @@ -60,25 +60,17 @@ class QueryResults { */ private final Map<Keyword, List<KeywordHit>> results = new HashMap<>(); - /** - * The list of keywords - */ - // TODO: This is redundant. The keyword list is in the query. - private final KeywordList keywordList; + - QueryResults(KeywordSearchQuery query, KeywordList keywordList) { + QueryResults(KeywordSearchQuery query) { this.keywordSearchQuery = query; - this.keywordList = keywordList; } void addResult(Keyword keyword, List<KeywordHit> hits) { results.put(keyword, hits); } - // TODO: This is redundant. The keyword list is in the query. - KeywordList getKeywordList() { - return keywordList; - } + KeywordSearchQuery getQuery() { return keywordSearchQuery; @@ -129,7 +121,7 @@ class QueryResults { if (hitDisplayStr.length() > 50) { hitDisplayStr = hitDisplayStr.substring(0, 49) + "..."; } - subProgress.progress(keywordList.getName() + ": " + hitDisplayStr, unitProgress); + subProgress.progress(keywordSearchQuery.getKeywordList().getName() + ": " + hitDisplayStr, unitProgress); } for (KeywordHit hit : getOneHitPerObject(keyword)) { @@ -138,7 +130,11 @@ class QueryResults { if (StringUtils.isBlank(snippet)) { final String snippetQuery = KeywordSearchUtil.escapeLuceneQuery(termString); try { - //this doesn't work for regex queries... + /* + * this doesn't work for regex queries... But that is + * okay because regex queries always have snippets made + * from the content_str field we pull back from Solr + */ snippet = LuceneQuery.querySnippet(snippetQuery, hit.getSolrObjectId(), hit.getChunkId(), !keywordSearchQuery.isLiteral(), true); } catch (NoOpenCoreException e) { logger.log(Level.WARNING, "Error querying snippet: " + snippetQuery, e); //NON-NLS @@ -149,16 +145,14 @@ class QueryResults { continue; } } - if (snippet != null) { - KeywordCachedArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard(keyword, hit, snippet, keywordList.getName()); - if (writeResult != null) { - newArtifacts.add(writeResult.getArtifact()); - if (notifyInbox) { - writeSingleFileInboxMessage(writeResult, hit.getContent()); - } - } else { - logger.log(Level.WARNING, "BB artifact for keyword hit not written, file: {0}, hit: {1}", new Object[]{hit.getContent(), keyword.toString()}); //NON-NLS + KeywordCachedArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard(keyword, hit, snippet, keywordSearchQuery.getKeywordList().getName()); + if (writeResult != null) { + newArtifacts.add(writeResult.getArtifact()); + if (notifyInbox) { + writeSingleFileInboxMessage(writeResult, hit.getContent()); } + } else { + logger.log(Level.WARNING, "BB artifact for keyword hit not written, file: {0}, hit: {1}", new Object[]{hit.getContent(), keyword.toString()}); //NON-NLS } } ++unitProgress; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index ea5e330ca9..c7004b1fa7 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -81,7 +81,7 @@ final class RegexQuery implements KeywordSearchQuery { private final Keyword originalKeyword; // The regular expression originalKeyword used to perform the search. private String field = Server.Schema.CONTENT_STR.toString(); private final String keywordString; - static final private int MAX_RESULTS = 512; + static final private int MAX_RESULTS_PER_CURSOR_MARK = 512; private boolean escaped; private String escapedQuery; @@ -154,7 +154,6 @@ final class RegexQuery implements KeywordSearchQuery { @Override public QueryResults performQuery() throws NoOpenCoreException { - QueryResults results = new QueryResults(this, keywordList); final Server solrServer = KeywordSearch.getServer(); SolrQuery solrQuery = new SolrQuery(); @@ -187,12 +186,12 @@ final class RegexQuery implements KeywordSearchQuery { .map(KeywordQueryFilter::toString) .forEach(solrQuery::addFilterQuery); - solrQuery.setRows(MAX_RESULTS); + solrQuery.setRows(MAX_RESULTS_PER_CURSOR_MARK); // Setting the sort order is necessary for cursor based paging to work. solrQuery.setSort(SortClause.asc(Server.Schema.ID.toString())); String cursorMark = CursorMarkParams.CURSOR_MARK_START; - SolrDocumentList resultList = null; + SolrDocumentList resultList ; boolean allResultsProcessed = false; while (!allResultsProcessed) { @@ -218,15 +217,14 @@ final class RegexQuery implements KeywordSearchQuery { } cursorMark = nextCursorMark; } catch (KeywordSearchModuleException ex) { - LOGGER.log(Level.SEVERE, "Error executing Lucene Solr Query: " + keywordString, ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Error executing Regex Solr Query: " + keywordString, ex); //NON-NLS MessageNotifyUtil.Notify.error(NbBundle.getMessage(Server.class, "Server.query.exception.msg", keywordString), ex.getCause().getMessage()); } } - + QueryResults results = new QueryResults(this); for (Keyword k : hitsMultiMap.keySet()) { results.addResult(k, hitsMultiMap.get(k)); } - return results; } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java index 2090aa3187..ce0d07bc78 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SearchRunner.java @@ -590,7 +590,7 @@ public final class SearchRunner { // Create a new (empty) QueryResults object to hold the most recently // found hits. - QueryResults newResults = new QueryResults(queryResult.getQuery(), queryResult.getKeywordList()); + QueryResults newResults = new QueryResults(queryResult.getQuery()); // For each keyword represented in the results. for (Keyword keyword : queryResult.getKeywords()) { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java index 44cc8ab5e9..7ecfdbe35a 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java @@ -280,7 +280,7 @@ final class TermsComponentQuery implements KeywordSearchQuery { /* * Do a term query for each term that matched the regex. */ - QueryResults results = new QueryResults(this, keywordList); + QueryResults results = new QueryResults(this); for (Term term : terms) { /* * If searching for credit card account numbers, do a Luhn check on diff --git a/build.xml b/build.xml index 55951fb079..9288184ea1 100755 --- a/build.xml +++ b/build.xml @@ -8,7 +8,7 @@ <import file="nbproject/build-impl.xml"/> <!-- IMPORTANT: nbproject/platform.properties has a netbeans-plat-version property that MUST be kept in sync (manually) --> - <property name="netbeans-plat-version" value="8.1" /> + <property name="netbeans-plat-version" value="8.2" /> <property name="nbplatform.active.dir" value="${basedir}/netbeans-plat/${netbeans-plat-version}" /> <!-- Supported java versions.--> <condition property="supported-java-versions"> diff --git a/nbproject/platform.properties b/nbproject/platform.properties index 9d31c5e1e0..07a18d325c 100644 --- a/nbproject/platform.properties +++ b/nbproject/platform.properties @@ -1,7 +1,7 @@ branding.token=autopsy # Version of platform that is automatically downloaded # IMPORTANT: autopsy/build.xml has a netbeans-plat-version property that MUST be kept in sync (manually) -netbeans-plat-version=8.1 +netbeans-plat-version=8.2 suite.dir=${basedir} nbplatform.active.dir=${suite.dir}/netbeans-plat/${netbeans-plat-version} harness.dir=${nbplatform.active.dir}/harness diff --git a/thirdparty/junit/8.2/junit.zip b/thirdparty/junit/8.2/junit.zip new file mode 100644 index 0000000000..db1b83aea2 Binary files /dev/null and b/thirdparty/junit/8.2/junit.zip differ
"); //NON-NLS - buffer.append(Bundle.ArtifactStringContent_attrsTableHeader_attribute()); - buffer.append(""); //NON-NLS + buffer.append(""); //NON-NLS + buffer.append(Bundle.ArtifactStringContent_attrsTableHeader_type()); + buffer.append(""); //NON-NLS buffer.append(Bundle.ArtifactStringContent_attrsTableHeader_value()); - buffer.append(""); //NON-NLS + buffer.append(""); //NON-NLS buffer.append(Bundle.ArtifactStringContent_attrsTableHeader_sources()); - buffer.append("